From e3006a22f50e6d5fd968f37a87b46f988b560ae1 Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Fri, 5 Jun 2020 12:03:25 +0100 Subject: [PATCH 1/6] Basic cut of DebugTypeInfo interface and implementation --- .../objectfile/debugentry/DebugInfoBase.java | 8 + .../debuginfo/DebugInfoProvider.java | 80 ++++++ .../image/NativeImageDebugInfoProvider.java | 240 +++++++++++++++++- 3 files changed, 320 insertions(+), 8 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 8cca4480b555..f4a3d563839b 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 @@ -27,6 +27,7 @@ package com.oracle.objectfile.debugentry; import com.oracle.objectfile.debuginfo.DebugInfoProvider; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugTypeInfo.DebugTypeKind; import org.graalvm.compiler.debug.DebugContext; import java.nio.ByteOrder; @@ -123,6 +124,13 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { */ stringTable.uniqueDebugString(""); + debugInfoProvider.typeInfoProvider().forEach(debugTypeInfo -> debugTypeInfo.debugContext((debugContext) -> { + String typeName = debugTypeInfo.typeName().replaceAll("\\$", "."); + DebugTypeKind typeKind = debugTypeInfo.typeKind(); + + debugContext.log(DebugContext.INFO_LEVEL, "%s type %s ", typeKind.toString(), typeName); + })); + debugInfoProvider.codeInfoProvider().forEach(debugCodeInfo -> debugCodeInfo.debugContext((debugContext) -> { /* * primary file name and full method name need to be written to the debug_str section 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 89da1bc263ad..2d358288845f 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 @@ -42,6 +42,86 @@ public interface DebugInfoProvider { * Access details of a specific type. */ interface DebugTypeInfo { + enum DebugTypeKind { + PRIMITIVE, + ENUM, + INSTANCE, + INTERFACE, + ARRAY; + + @Override + public String toString() { + switch (this) { + case PRIMITIVE: + return "primitive"; + case ENUM: + return "enum"; + case INSTANCE: + return "instance"; + case INTERFACE: + return "interface"; + case ARRAY: + return "array"; + default: + return "???"; + } + } + }; + + void debugContext(Consumer action); + + /** + * @return the fully qualified name of the debug type. + */ + String typeName(); + + DebugTypeKind typeKind(); + + int size(); + } + + interface DebugEnumTypeInfo extends DebugInstanceTypeInfo { + } + + interface DebugInstanceTypeInfo extends DebugTypeInfo { + int headerSize(); + Stream fieldInfoProvider(); + } + + interface DebugInterfaceTypeInfo extends DebugTypeInfo { + } + + interface DebugArrayTypeInfo extends DebugTypeInfo { + int headerSize(); + int lengthOffset(); + String elementType(); + } + + interface DebugPrimitiveTypeInfo extends DebugTypeInfo { + /* + * NUMERIC excludes boolean and void + */ + int FLAG_NUMERIC = 1 << 0; + /* + * INTEGRAL excludes float and double + */ + int FLAG_INTEGRAL = 1 << 1; + /* + * SIGNED excludes char + */ + int FLAG_SIGNED = 1 << 2; + int bitCount(); + int flags(); + } + + interface DebugFieldInfo { + String ownerType(); + + String valueType(); + + int offset(); + + int size(); } /** 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 211865717b70..915eac731684 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 @@ -29,25 +29,35 @@ import static com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFrameSizeChange.Type.EXTEND; import java.nio.file.Path; +import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.function.Consumer; import java.util.stream.Stream; -import com.oracle.svm.core.SubstrateOptions; -import org.graalvm.compiler.code.CompilationResult; -import org.graalvm.compiler.code.SourceMapping; -import org.graalvm.compiler.debug.DebugContext; -import org.graalvm.compiler.graph.NodeSourcePosition; -import org.graalvm.nativeimage.ImageSingletons; - import com.oracle.graal.pointsto.infrastructure.OriginalClassProvider; import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.objectfile.debuginfo.DebugInfoProvider; + +import com.oracle.svm.core.SubstrateOptions; +import com.oracle.svm.core.config.ConfigurationValues; +import com.oracle.svm.core.config.ObjectLayout; import com.oracle.svm.core.graal.code.SubstrateBackend; import com.oracle.svm.hosted.image.sources.SourceManager; +import com.oracle.svm.hosted.meta.HostedArrayClass; +import com.oracle.svm.hosted.meta.HostedField; import com.oracle.svm.hosted.meta.HostedMethod; import com.oracle.svm.hosted.meta.HostedType; +import com.oracle.svm.hosted.meta.HostedInstanceClass; +import com.oracle.svm.hosted.meta.HostedInterface; +import com.oracle.svm.hosted.meta.HostedPrimitiveType; + +import org.graalvm.compiler.code.CompilationResult; +import org.graalvm.compiler.code.SourceMapping; +import org.graalvm.compiler.debug.DebugContext; +import org.graalvm.compiler.graph.NodeSourcePosition; +import org.graalvm.nativeimage.ImageSingletons; + import jdk.vm.ci.meta.LineNumberTable; import jdk.vm.ci.meta.ResolvedJavaMethod; @@ -71,7 +81,7 @@ class NativeImageDebugInfoProvider implements DebugInfoProvider { @Override public Stream typeInfoProvider() { - return Stream.empty(); + return heap.getUniverse().getTypes().stream().map(this::createDebugTypeInfo); } @Override @@ -84,6 +94,215 @@ public Stream dataInfoProvider() { return Stream.empty(); } + static ObjectLayout OBJECTLAYOUT = ConfigurationValues.getObjectLayout(); + + private abstract class NativeImageDebugTypeInfo implements DebugTypeInfo { + + protected final HostedType hostedType; + protected final ResolvedJavaType javaType; + protected final Class clazz; + + protected NativeImageDebugTypeInfo(HostedType hostedType) { + this.hostedType = hostedType; + this.javaType = hostedType.getWrapped(); + if (hostedType instanceof OriginalClassProvider) { + clazz = ((OriginalClassProvider) hostedType).getJavaClass(); + } else { + clazz = null; + } + } + + @SuppressWarnings("try") + @Override + public void debugContext(Consumer action) { + try (DebugContext.Scope s = debugContext.scope("DebugTypeInfo", typeName())) { + action.accept(debugContext); + } catch (Throwable e) { + throw debugContext.handle(e); + } + } + + @Override + public String typeName() { + return hostedType.toJavaName(); + } + + @Override + public int size() { + if (hostedType instanceof HostedInstanceClass) { + return ((HostedInstanceClass) hostedType).getInstanceSize(); + } else { + return hostedType.getStorageKind().getByteCount(); + } + } + } + + private class NativeImageDebugEnumTypeInfo extends NativeImageDebugInstanceTypeInfo implements DebugEnumTypeInfo { + HostedInstanceClass enumClass; + + NativeImageDebugEnumTypeInfo(HostedInstanceClass enumClass) { + super(enumClass); + this.enumClass = enumClass; + } + + public DebugTypeKind typeKind() { + return DebugTypeKind.ENUM; + } + } + + private class NativeImageDebugInstanceTypeInfo extends NativeImageDebugTypeInfo implements DebugInstanceTypeInfo { + NativeImageDebugInstanceTypeInfo(HostedType hostedType) { + super(hostedType); + } + + public DebugTypeKind typeKind() { + return DebugTypeKind.INSTANCE; + } + + @Override + public int headerSize() { + return OBJECTLAYOUT.getFirstFieldOffset(); + } + @Override + public Stream fieldInfoProvider() { + return Arrays.stream(hostedType.getInstanceFields(true)).map(this::createDebugFieldInfo); + } + + protected NativeImageDebugFieldInfo createDebugFieldInfo(HostedField field) { + return new NativeImageDebugFieldInfo(field); + } + + protected class NativeImageDebugFieldInfo implements DebugFieldInfo { + private final HostedField field; + + NativeImageDebugFieldInfo(HostedField field) { + this.field = field; + } + + @Override + public String ownerType() { + return typeName(); + } + + @Override + public String valueType() { + return field.getType().toJavaName(); + } + + @Override + public int offset() { + return field.getLocation(); + } + + @Override + public int size() { + return OBJECTLAYOUT.sizeInBytes(field.getType().getStorageKind()); + } + } + } + + private class NativeImageDebugInterfaceTypeInfo extends NativeImageDebugTypeInfo implements DebugInterfaceTypeInfo { + HostedInterface interfaceClass; + NativeImageDebugInterfaceTypeInfo(HostedInterface interfaceClass) { + super(interfaceClass); + this.interfaceClass = interfaceClass; + } + + public DebugTypeKind typeKind() { + return DebugTypeKind.INTERFACE; + } + } + + private class NativeImageDebugArrayTypeInfo extends NativeImageDebugTypeInfo implements DebugArrayTypeInfo { + HostedArrayClass arrayClass; + + NativeImageDebugArrayTypeInfo(HostedArrayClass arrayClass) { + super(arrayClass); + this.arrayClass = arrayClass; + } + + public DebugTypeKind typeKind() { + return DebugTypeKind.ARRAY; + } + + @Override + public int headerSize() { + return OBJECTLAYOUT.getArrayBaseOffset(arrayClass.getComponentType().getStorageKind()); + } + + @Override + public int lengthOffset() { + return OBJECTLAYOUT.getArrayLengthOffset(); + } + + @Override + public String elementType() { + return arrayClass.getComponentType().toJavaName(); + } + } + + private class NativeImageDebugPrimitiveTypeInfo extends NativeImageDebugTypeInfo implements DebugPrimitiveTypeInfo { + private final HostedPrimitiveType primitiveType; + + NativeImageDebugPrimitiveTypeInfo(HostedPrimitiveType primitiveType) { + super(primitiveType); + this.primitiveType = primitiveType; + } + + public DebugTypeKind typeKind() { + return DebugTypeKind.PRIMITIVE; + } + + @Override + public int bitCount() { + return primitiveType.getStorageKind().getBitCount(); + } + + @Override + public int flags() { + char typeChar = primitiveType.getStorageKind().getTypeChar(); + switch (typeChar) { + case 'B': + case 'S': + case 'I': + case 'J': + { + return FLAG_NUMERIC | FLAG_INTEGRAL | FLAG_SIGNED; + } + case 'C': + { + return FLAG_NUMERIC | FLAG_INTEGRAL; + } + case 'F': + case 'D': + { + return FLAG_NUMERIC; + } + default: + { + assert typeChar == 'V' || typeChar == 'Z'; + return 0; + } + } + } + } + + private NativeImageDebugTypeInfo createDebugTypeInfo(HostedType hostedType) { + if (hostedType.isEnum()) { + return new NativeImageDebugEnumTypeInfo((HostedInstanceClass) hostedType); + } else if (hostedType.isInstanceClass()) { + return new NativeImageDebugInstanceTypeInfo((HostedInstanceClass) hostedType); + } else if (hostedType.isInterface()) { + return new NativeImageDebugInterfaceTypeInfo((HostedInterface) hostedType); + } else if (hostedType.isArray()) { + return new NativeImageDebugArrayTypeInfo((HostedArrayClass) hostedType); + } else if (hostedType.isPrimitive()) { + return new NativeImageDebugPrimitiveTypeInfo((HostedPrimitiveType) hostedType); + } else { + throw new RuntimeException("Unknown type kind " + hostedType.getName()); + } + } + /** * Implementation of the DebugCodeInfo API interface that allows code info to be passed to an * ObjectFile when generation of debug info is enabled. @@ -326,9 +545,14 @@ private void computeFullFilePath() { clazz = ((OriginalClassProvider) declaringClass).getJavaClass(); } /* +<<<<<<< HEAD * HostedType wraps an AnalysisType and both HostedType and AnalysisType punt calls to * getSourceFilename to the wrapped class so for consistency we need to do the path * lookup relative to the doubly unwrapped HostedType or singly unwrapped AnalysisType. +======= + * HostedType and HostedType punt calls to getSourceFilename to the wrapped class so + * for consistency we need to do the path lookup relative to the wrapped class. +>>>>>>> e5e46875909... basic cut of DebugTypeInfo interface and implementation */ if (declaringClass instanceof HostedType) { declaringClass = ((HostedType) declaringClass).getWrapped(); From b307ee84155fb340719a658a7be2cc9159c49c02 Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Fri, 21 Aug 2020 17:32:42 +0100 Subject: [PATCH 2/6] Update debug info model to include details of types first cut of debug info model and DWARF generator employing class layouts rebase on updated master + doc and style fixes fix problem with method name computation test type and instance printing from debuginfotest script building without isolates fix method name and file lookup update documentation to include details of debug type info support apply consistent file lookup rules --- substratevm/DebugInfo.md | 287 +++- substratevm/mx.substratevm/mx_substratevm.py | 1 + substratevm/mx.substratevm/testhello.py | 187 ++- .../objectfile/debugentry/ArrayTypeEntry.java | 61 + .../objectfile/debugentry/ClassEntry.java | 257 ++-- .../objectfile/debugentry/DebugInfoBase.java | 248 +++- .../objectfile/debugentry/DirEntry.java | 2 +- .../objectfile/debugentry/EnumClassEntry.java | 43 + .../objectfile/debugentry/FieldEntry.java | 46 + .../objectfile/debugentry/FileEntry.java | 22 +- .../debugentry/HeaderTypeEntry.java | 52 + .../debugentry/InterfaceClassEntry.java | 65 + .../objectfile/debugentry/MemberEntry.java | 97 ++ .../objectfile/debugentry/MethodEntry.java | 97 ++ .../objectfile/debugentry/PrimaryEntry.java | 9 +- .../debugentry/PrimitiveTypeEntry.java | 98 ++ .../oracle/objectfile/debugentry/Range.java | 131 +- .../objectfile/debugentry/StringEntry.java | 5 +- .../debugentry/StructureTypeEntry.java | 107 ++ .../objectfile/debugentry/TypeEntry.java | 105 ++ .../debuginfo/DebugInfoProvider.java | 132 +- .../elf/dwarf/DwarfARangesSectionImpl.java | 19 +- .../elf/dwarf/DwarfAbbrevSectionImpl.java | 1139 ++++++++++++++- .../objectfile/elf/dwarf/DwarfDebugInfo.java | 461 +++++- .../elf/dwarf/DwarfFrameSectionImpl.java | 8 +- .../elf/dwarf/DwarfInfoSectionImpl.java | 1248 ++++++++++++++--- .../elf/dwarf/DwarfLineSectionImpl.java | 29 +- .../elf/dwarf/DwarfSectionImpl.java | 208 ++- .../elf/dwarf/DwarfStrSectionImpl.java | 19 +- .../pecoff/cv/CVLineRecordBuilder.java | 2 +- .../pecoff/cv/CVSymbolSubsectionBuilder.java | 2 +- .../image/NativeImageDebugInfoProvider.java | 755 ++++++++-- .../hosted/substitute/SubstitutionField.java | 8 + 33 files changed, 5269 insertions(+), 681 deletions(-) create mode 100644 substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ArrayTypeEntry.java create mode 100644 substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/EnumClassEntry.java create mode 100644 substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/FieldEntry.java create mode 100644 substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/HeaderTypeEntry.java create mode 100644 substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/InterfaceClassEntry.java create mode 100644 substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/MemberEntry.java create mode 100644 substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/MethodEntry.java create mode 100644 substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/PrimitiveTypeEntry.java create mode 100644 substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/StructureTypeEntry.java create mode 100644 substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/TypeEntry.java diff --git a/substratevm/DebugInfo.md b/substratevm/DebugInfo.md index 0e69446d1c1a..efae00fa9981 100644 --- a/substratevm/DebugInfo.md +++ b/substratevm/DebugInfo.md @@ -12,6 +12,12 @@ The resulting image should contain code (method) debug records in a format the GNU Debugger (GDB) understands (Windows support is still under development). At present it makes no difference which positive value is supplied to the `GenerateDebugInfo` option. +Note that it is currently recommended to pass flag `-H:-UseIsolates` on the command +line whenever debug info generation is enabled. When isolates are enabled field +values of Object (reference) type are stored as offsets from the heap base rather +than direct addresses and this confuses the debugger when it tries to read through +object fields. This problem should be corrected in a later release. + The `GenerateDebugInfo` option also enables caching of sources for any JDK runtime classes, GraalVM classes, and application classes which can be located during native image generation. By default, the cache is @@ -41,6 +47,7 @@ javac -cp apps/greeter/classes \ --source-path apps/hello/src \ -d apps/hello/classes org/my/hello/Hello.java native-image -H:GenerateDebugInfo=1 \ + -H:-UseIsolates \ -H:DebugInfoSourceSearchPath=apps/hello/src \ -H:DebugInfoSourceSearchPath=apps/greeter/src \ -cp apps/hello/classes:apps/greeter/classes org.my.hello.Hello @@ -65,6 +72,7 @@ command specifies the same target but employs an absolute path: ```shell SOURCE_CACHE_ROOT=$PWD/sources native-image -H:GenerateDebugInfo=1 \ + -H:-UseIsolates \ -H:DebugInfoSourceCacheRoot=$SOURCE_CACHE_ROOT \ -H:DebugInfoSourceSearchPath=apps/hello/target/hello-sources.jar,apps/greeter/target/greeter-sources.jar \ -cp apps/target/hello.jar:apps/target/greeter.jar \ @@ -76,7 +84,7 @@ created during population of the cache. Note that in all the examples above the `DebugInfoSourceSearchPath` options are actually redundant. In the first case, the classpath entries for _apps/hello/classes_ and _apps/greeter/classes_ will be used -to derive the default search roots `apps/hello/src` and +to derive the default search roots _apps/hello/src_ and _apps/greeter/src_. In the second case, the classpath entries for _apps/target/hello.jar_ and _apps/target/greeter.jar_ will be used to derive the default search roots _apps/target/hello-sources.jar_ and @@ -90,12 +98,289 @@ The currently implemented features include: - break points configured by file and line, or by method name - single stepping by line including both into and over function calls - stack backtraces (not including frames detailing inlined code) + - printing of primitive values + - structured (field by field) printing of Java objects + - casting/printing objects at different levels of generality + - access through object networks via path expressions + - reference by name to methods and static field data Note that single stepping within a compiled method includes file and line number info for inlined code, including inlined GraalVM methods. So, GDB may switch files even though you are still in the same compiled method. +### Special considerations for debugging Java from GDB + +GDB does not currently include support for debugging of Java programs. +In consequence, debug capability has been implemented by generating debug +info that models the Java program as an equivalent C++ program. Java +class, array and interface references are modelled as pointers to +underlying C++ (class/struct) layout types. + +So, for example in the DWARF debug info model `java.lang.String` names +a pointer type to a class (layout) type named `_java.lang.String`. The +layout type declares fields like `hash` of type `int` and `value` of +type `byte[]` and methods like `String(byte[])`, `charAt(int)`, +etc. It also inherits fields and methods from class (layout) type +_java.lang.Object using C++ public inheritance. The latter in turn +inherits standard oop header fields from a special struct class named +_objhdr which includes a pointer to the object's class. + +The ptype command can be used to print details of a specific type. Note +that the java type name must be specified in quotes because to escape the +embedded `.` characters. + +``` +(gdb) ptype +ptype 'java.lang.String' +type = class _java.lang.String : public _java.lang.Object { + private: + byte [] value; + int hash; + byte coder; + public: + void String(byte []); + . . . + char charAt(int); + . . . +} * +``` + +The print command can be used to print the contents of a referenced object +field by field. Note how a cast is used to convert a raw memory address to +a reference of a specific Java type. + +``` +(gdb) print/x *('java.lang.String')0x7ffff7c01058 +$1 = { + <_java.lang.Object> = { + <_objhdr> = { + hub = 0x90c670 + }, }, + members of _java.lang.String: + value = 0x7ffff7c01198, + hash = 0x0, + coder = 0x0 +} +``` + +The hub field in the object header is actually a reference of type +`java.lang/Class`. Note that the field is typed using the pointer type +rather than the underlying layout type `_java.lang.Class`. + +``` +(gdb) ptype _objhdr +type = struct _objhdr { + java.lang.Class hub; +} +``` + +Given a putative object reference it is possible to identify it's type by +printing the contents of the String referenced from the hub's name field. +First the value is cast to an object reference. Then a path expression is +used to dereference through the the hub field and the hub's name field to +the `byte[]` value array located in the name String. + +``` +(gdb) print/x ((_objhdr *)$rdi) +$19 = 0x7ffff7c01028 +(gdb) print *$19->hub->name->value +$21 = { + <_arrhdrB> = { + hub = 0x90e0a8, + len = 19, + idHash = 797697923 + }, + members of _byte []: + data = 0x9046d8 "[Ljava.lang.String;" +} +``` + +A simpler equivalent is as follows + +``` +(gdb) x/s $19->hub->name->value->data +0x9046d8: "[Ljava.lang.String;" +``` + +The value in register rdx is obviously a reference to a String array. +Casting it to this type shows it has length 1. + +``` +(gdb) print *('java.lang.String[]')$rdi +$40 = { + <_arrhdrA> = { + hub = 0x906b30, + len = 1, + idHash = 0 + }, + members of _java.lang.String[]: + data = 0x7ffff7c01038 +} + +``` + +Note that attempting to print the hub name for an invalid reference +will fail. + +``` +(gdb) p/x (_objhdr *)$rdx +$37 = 0x2 +(gdb) x/s $37->hub->name->value->data +Cannot access memory at address 0x2 +``` + +Array type layouts are modelled with an array header whose fields +include the hub, array length, idhash plus type-specific padding +followed by a (zero length) array inlined into the object. The common +prefix for the array header and object header structs means that they +are all able to be viewed as oops. + +``` +(gdb) ptype 'java.lang.String[]' +type = struct _java.lang.String[] : public _arrhdrA { + java.lang.String data[0]; +} * +``` + +Interfaces layouts are modelled as union types whose members are the +layouts for all the classes which implement the interfacse. + +``` +(gdb) ptype 'java.lang.CharSequence' +type = union _java.lang.CharSequence { + _java.lang.AbstractStringBuilder _java.lang.AbstractStringBuilder; + _java.lang.StringBuffer _java.lang.StringBuffer; + _java.lang.StringBuilder _java.lang.StringBuilder; + _java.lang.String _java.lang.String; + _java.nio.CharBuffer _java.nio.CharBuffer; +} * +``` + +Given a reference typed to an interface it can be resolved to the +relevant class type by viewing it through the relevant union element +``` +(gdb) print (('java.lang.String[]')$rdi)->data[0] +$41 = (java.lang.String) 0x7ffff7c01058 +(gdb) print ('java.lang.CharSequence')$41 +$42 = (java.lang.CharSequence) 0x7ffff7c01058 +(gdb) x/s ((_objhdr*)$42)->hub->name->value->data +0x7d97e0: "java.lang.String\250", +(gdb) print $42->'_java.lang.String' +$45 = { + <_java.lang.Object> = { + <_objhdr> = { + hub = 0x90c670 + }, }, + members of _java.lang.String: + value = 0x7ffff7c01198, + hash = 0, + coder = 0 '\000' +} +``` + +The current debug info model does not include the location info needed +to allow symbolic names for local vars and parameter vars to be resolved +to primitive values or object references. However, the debugger does +understand method names and static field names. + +The following command places a breakpoint on the main entry point for +class `Hello`. Note that the GDB considers the method to belong to the +class (layout) type `_Hello` rather than the pointer type `Hello`. Also, +since GDB thinks this is a C++ program it uses the `::` separator to +separate the method name from the class name. + +``` +(gdb) info func ::main +All functions matching regular expression "::main": + +File Hello.java: + void _Hello::main(java.lang.String[]); +(gdb) b _Hello::main +Breakpoint 1 at 0x4065a0: file Hello.java, line 43. +(gdb) x/4i _Hello::main + 0x4065a0 <_Hello::main(java.lang.String[])>: sub $0x8,%rsp + 0x4065a4 <_Hello::main(java.lang.String[])+4>: cmp 0x8(%r15),%rsp + 0x4065a8 <_Hello::main(java.lang.String[])+8>: jbe 0x4065fd <_Hello::main(java.lang.String[])+93> + 0x4065ae <_Hello::main(java.lang.String[])+14>: callq 0x406050 <_Hello$Greeter::greeter(java.lang.String[])> +``` + + An example of a static field containing Object data is provided by + the static field `powerCache` in class `BigInteger` + +``` +(gdb) ptype 'java.math.BigInteger' +type = class _java.math.BigInteger : public _java.lang.Number { + public: + int [] mag; + int signum; + private: + int bitLengthPlusOne; + int lowestSetBitPlusTwo; + int firstNonzeroIntNumPlusTwo; + static java.math.BigInteger[][] powerCache; + . . . +} * +(gdb) info var powerCache +All variables matching regular expression "powerCache": + +File java/math/BigInteger.java: + java.math.BigInteger[][] _java.math.BigInteger::powerCache; +``` + +The static variable name can be used to refer to the value stored in +this field. Note also that the address operator can be used identify +the location (address) of the field in the heap. + +``` +(gdb) p '_java.math.BigInteger'::powerCache +$56 = (java.math.BigInteger[][]) 0xa6fd98 +(gdb) p &'_java.math.BigInteger'::powerCache +$55 = (java.math.BigInteger[][] *) 0xa6fbc8 +``` + +gdb dereferences through symbolic names for static fields to access +the primitive value or object stored in the field + +``` +(gdb) p *'_java.math.BigInteger'::powerCache +$33 = { + <_arrhdrA> = { + hub = 0x9ab3d0, + len = 37, + idHash = 489620191 + }, + members of _java.math.BigInteger[][]: + data = 0xa6fda8 +} +(gdb) p '_java.math.BigInteger'::powerCache->data[0]@4 +$51 = {0x0, 0x0, 0xc09378, 0xc09360} +(gdb) p *'_java.math.BigInteger'::powerCache->data[2] +$52 = { + <_arrhdrA> = { + hub = 0x919898, + len = 1, + idHash = 1796421813 + }, + members of _java.math.BigInteger[]: + data = 0xc09388 +} +(gdb) p *'_java.math.BigInteger'::powerCache->data[2]->data[0] +$53 = { + <_java.lang.Number> = { + <_java.lang.Object> = { + <_objhdr> = { + hub = 0x919c70 + }, }, }, + members of _java.math.BigInteger: + mag = 0xa5b030, + signum = 1, + bitLengthPlusOne = 0, + lowestSetBitPlusTwo = 0, + firstNonzeroIntNumPlusTwo = 0 +} +``` + ### Identifying the Location of Source Code One goal of the implementation is to make it simple to configure your diff --git a/substratevm/mx.substratevm/mx_substratevm.py b/substratevm/mx.substratevm/mx_substratevm.py index 442fe9e2d0bd..52a92d14dc6d 100644 --- a/substratevm/mx.substratevm/mx_substratevm.py +++ b/substratevm/mx.substratevm/mx_substratevm.py @@ -755,6 +755,7 @@ def _debuginfotest(native_image, path, build_only, args): '-cp', classpath('com.oracle.svm.test'), '-Dgraal.LogFile=graal.log', '-g', + '-H:-SpawnIsolates', '-H:DebugInfoSourceSearchPath=' + sourcepath, '-H:DebugInfoSourceCacheRoot=' + join(path, 'sources'), 'hello.Hello'] + args diff --git a/substratevm/mx.substratevm/testhello.py b/substratevm/mx.substratevm/testhello.py index 3b1e011018fc..21d51b4d7b27 100644 --- a/substratevm/mx.substratevm/testhello.py +++ b/substratevm/mx.substratevm/testhello.py @@ -132,9 +132,11 @@ def test(): wildcard_pattern = '.*' # disable prompting to continue output execute("set pagination off") - # set a break point at hello.Hello.main - # expect "Breakpoint 1 at 0x[0-9a-f]+: file hello.Hello.java, line 67." - exec_string = execute("break hello.Hello.main") + # 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." + exec_string = execute("break _hello.Hello::main") rexp = r"Breakpoint 1 at %s: file hello/Hello\.java, line 67\."%address_pattern checker = Checker('break main', rexp) checker.check(exec_string) @@ -149,36 +151,73 @@ def test(): 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:67" # 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"#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) + checker = Checker("backtrace hello.Hello::main", + [r"#0%s_hello\.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) ]) checker.check(exec_string, skip_fails=False) + # print the contents of the arguments array which will be in rdi + exec_string = execute("print /x *(('java.lang.String[]')$rdi)") + checker = Checker("print String[] args", + [r"%s = {"%(wildcard_pattern), + r"%s<_arrhdrA> = {"%(spaces_pattern), + r"%shub = %s,"%(spaces_pattern, address_pattern), + r"%slen = 0x0,"%(spaces_pattern), + r"%sidHash = %s"%(spaces_pattern, address_pattern), + r"%s},"%(spaces_pattern), + r"%smembers of _java\.lang\.String\[\]:"%(spaces_pattern), + r"%sdata = %s"%(spaces_pattern, address_pattern), + "}"]) + + checker.check(exec_string, skip_fails=False) + + # print the hub of the array and check it has a name field + exec_string = execute("print /x *(('java.lang.String[]')$rdi)->hub") + checker = Checker("print String[] hub", + [r"%s = {"%(wildcard_pattern), + r"%s<_java.lang.Object> = {"%(spaces_pattern), + r"%s<_objhdr> = {"%(spaces_pattern), + r"%shub = %s"%(spaces_pattern, address_pattern), + r"%s}, },"%(spaces_pattern), + r"%smembers of _java\.lang\.Class:"%(spaces_pattern), + r"%sname = %s"%(spaces_pattern, address_pattern), + "}"]) + + checker.check(exec_string, skip_fails=True) + + # print the hub name field and check it is String[] + # n.b. the expected String text is not necessarily null terminated + # so we need a wild card before the final quote + exec_string = execute("x/s (('java.lang.String[]')$rdi)->hub->name->value->data") + checker = Checker("print String[] hub name", + r"%s:%s\"\[Ljava.lang.String;.*\""%(address_pattern, spaces_pattern)) + checker.check(exec_string, skip_fails=False) + # look up PrintStream.println methods # 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)(void);" - # expect " void java.io.PrintStream.println(java.lang.String)(void);" - 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\":", + # 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\\)\\(void\\);", - # "[ \t]*void java.io.PrintStream\.println\\(java\\.lang\\.String\\)\\(void\\);", + # "[ \t]*void _java.io.PrintStream::println\\(java\\.lang\\.Object\\);", + # "[ \t]*void _java.io.PrintStream::println\\(java\\.lang\\.String\\);", # ]) - checker = Checker("info func java.io.PrintStream.println", - r"%svoid java.io.PrintStream\.println\(java\.lang\.String\)"%maybe_spaces_pattern) + checker = Checker("info func java.io.PrintStream::println", + r"%svoid _java.io.PrintStream::println\(java\.lang\.String\)"%maybe_spaces_pattern) checker.check(exec_string) # set a break point at PrintStream.println(String) # expect "Breakpoint 2 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)") + exec_string = execute("break _java.io.PrintStream::println(java.lang.String)") rexp = r"Breakpoint 2 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) @@ -190,28 +229,81 @@ def test(): # expect "34 if (args.length == 0) {" exec_string = execute("list") rexp = r"34%sif \(args\.length == 0\) {"%spaces_pattern - checker = Checker('list hello.Hello.Greeter.greeter', rexp) + checker = Checker('list hello.Hello$Greeter.greeter', rexp) + checker.check(exec_string, skip_fails=False) + + # print details of greeter types + exec_string = execute("ptype 'hello.Hello$NamedGreeter'") + rexp = [r"type = class _hello\.Hello\$NamedGreeter : public _hello\.Hello\$Greeter {", + r"%sprivate:"%(spaces_pattern), + r"%sjava\.lang\.String name;"%(spaces_pattern), + r"", + r"%spublic:"%(spaces_pattern), + r"%svoid greet\(void\);"%(spaces_pattern), + r"} \*"] + checker = Checker('ptype NamedGreeter', rexp) + checker.check(exec_string, skip_fails=False) + + exec_string = execute("ptype 'hello.Hello$Greeter'") + rexp = [r"type = class _hello\.Hello\$Greeter : public _java\.lang\.Object {", + r"%spublic:"%(spaces_pattern), + r"%sstatic hello\.Hello\$Greeter greeter\(java\.lang\.String\[\]\);"%(spaces_pattern), + r"} \*"] + + checker = Checker('ptype Greeter', rexp) checker.check(exec_string, skip_fails=False) + exec_string = execute("ptype 'java.lang.Object'") + rexp = [r"type = class _java\.lang\.Object : public _objhdr {", + r"%spublic:"%(spaces_pattern), + r"%svoid Object\(void\);"%(spaces_pattern), + r"%sboolean equals\(java\.lang\.Object\);"%(spaces_pattern), + r"%sprivate:"%(spaces_pattern), + r"%sint hashCode\(void\);"%(spaces_pattern), + r"%sjava\.lang\.String toString\(void\);"%(spaces_pattern), + r"} \*"] + + checker = Checker('ptype Object', rexp) + checker.check(exec_string, skip_fails=True) + + exec_string = execute("ptype _objhdr") + rexp = [r"type = struct _objhdr {", + r"%sjava\.lang\.Class hub;"%(spaces_pattern), + r"}"] + checker = Checker('ptype _objhdr', rexp) + checker.check(exec_string, skip_fails=True) + + checker = Checker('ptype _objhdr', rexp) + checker.check(exec_string, skip_fails=True) + + exec_string = execute("ptype _arrhdrA") + rexp = [r"type = struct _arrhdrA {", + r"%sjava\.lang\.Class hub;"%(spaces_pattern), + r"%sint len;"%(spaces_pattern), + r"%sint idHash;"%(spaces_pattern), + r"}"] + checker = Checker('ptype _objhdr', rexp) + 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]+" + # 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) + checker = Checker("backtrace hello.Hello.Greeter::greeter", + [r"#0%s_hello\.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) ]) checker.check(exec_string, skip_fails=False) # now step into inlined code execute("next") - # check we are still in hello.Hello.Greeter.greeter but no longer in hello.Hello.java + # check we are still in hello.Hello$Greeter.greeter but no longer in hello.Hello.java exec_string = execute("backtrace 1") checker = Checker("backtrace inline", - [r"#0%shello\.Hello\.Greeter\.greeter\(java\.lang\.String\[\]\)%s at (%s):%s"%(spaces_pattern, wildcard_pattern, package_file_pattern, digits_pattern)]) + [r"#0%s_hello\.Hello\$Greeter::greeter\(java\.lang\.String\[\]\)%s at (%s):%s"%(spaces_pattern, wildcard_pattern, package_file_pattern, digits_pattern)]) matches = checker.check(exec_string, skip_fails=False) # n.b. can only get back here with one match match = matches[0] @@ -224,11 +316,11 @@ def test(): # continue to next breakpoint 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]+" + # 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 = Checker("backtrace 1 PrintStream::println", + [r"#0%s_java\.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) # list current line @@ -250,7 +342,38 @@ def test(): checker = Checker('list println 2', rexp) checker.check(exec_string, skip_fails=False) - # continue to next breakpoint + # print the java.io.PrintStream instance and check its type + exec_string = execute("print /x *(('java.io.PrintStream')$rdi)") + checker = Checker("print DefaultGreeterSystem.out", + [r"%s = {"%(wildcard_pattern), + r"%s<_java.io.FilterOutputStream> = {"%(spaces_pattern), + r"%s<_java.io.OutputStream> = {"%(spaces_pattern), + r"%s<_java.lang.Object> = {"%(spaces_pattern), + r"%s<_objhdr> = {"%(spaces_pattern), + r"%shub = %s"%(spaces_pattern, address_pattern), + r"%s}, }, },"%(spaces_pattern), + r"%smembers of _java.io.FilterOutputStream:"%(spaces_pattern), + r"%sout = %s,"%(spaces_pattern, address_pattern), + r"%scloseLock = %s,"%(spaces_pattern, address_pattern), + r"%sclosed = 0x0"%(spaces_pattern), + r"%s},"%(spaces_pattern), + r"%smembers of _java.io.PrintStream:"%(spaces_pattern), + r"%sautoFlush = 0x1,"%(spaces_pattern), + r"%sclosing = 0x0,"%(spaces_pattern), + r"%stextOut = %s,"%(spaces_pattern, address_pattern), + r"%scharOut = %s"%(spaces_pattern, address_pattern), + r"}"]) + + checker.check(exec_string, skip_fails=True) + + # print the hub name field and check it is java.io.PrintStream + # n.b. the expected String text is not necessarily null terminated + # so we need a wild card before the final quote + exec_string = execute("x/s (('java.io.PrintStream')$rdi)->hub->name->value->data") + checker = Checker("print PrintStream hub name", + r"%s:%s\"java.io.PrintStream.*\""%(address_pattern, spaces_pattern)) + checker.check(exec_string, skip_fails=False) + print(execute("quit 0")) test() diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ArrayTypeEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ArrayTypeEntry.java new file mode 100644 index 000000000000..a4d0fa4292a9 --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ArrayTypeEntry.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2020, Red Hat Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.objectfile.debugentry; + +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugArrayTypeInfo; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugTypeInfo; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugTypeInfo.DebugTypeKind; +import org.graalvm.compiler.debug.DebugContext; + +public class ArrayTypeEntry extends TypeEntry { + private TypeEntry elementType; + private int headerSize; + private int lengthOffset; + + public ArrayTypeEntry(String typeName, int size) { + super(typeName, size); + } + + @Override + public DebugTypeKind typeKind() { + return DebugTypeKind.ARRAY; + } + + @Override + public void addDebugInfo(DebugInfoBase debugInfoBase, DebugTypeInfo debugTypeInfo, DebugContext debugContext) { + DebugArrayTypeInfo debugArrayTypeInfo = (DebugArrayTypeInfo) debugTypeInfo; + String elementTypeName = TypeEntry.canonicalize(debugArrayTypeInfo.elementType()); + elementType = debugInfoBase.lookupTypeEntry(elementTypeName); + this.headerSize = debugArrayTypeInfo.headerSize(); + this.lengthOffset = debugArrayTypeInfo.lengthOffset(); + debugContext.log("typename %s element type %s header size %d length offset %d\n", typeName, elementTypeName, headerSize, lengthOffset); + } + + public TypeEntry getElementType() { + return elementType; + } +} 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 41b549494e3a..4d9d528fe270 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 @@ -27,35 +27,49 @@ package com.oracle.objectfile.debugentry; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFrameSizeChange; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugMethodInfo; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugTypeInfo; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugInstanceTypeInfo; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugTypeInfo.DebugTypeKind; +import org.graalvm.compiler.debug.DebugContext; +import java.nio.file.Path; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; /** - * track debug info associated with a Java class. + * Track debug info associated with a Java class. */ -public class ClassEntry { +public class ClassEntry extends StructureTypeEntry { /** - * the name of the associated class. + * Details of this class's superclass. */ - private String className; + protected ClassEntry superClass; /** - * details of the associated file. + * Details of this class's interfaces. + */ + protected LinkedList interfaces; + /** + * Details of the associated file. */ private FileEntry fileEntry; /** - * a list recording details of all primary ranges included in this class sorted by ascending + * Details of methods located in this instance. + */ + protected List methods; + /** + * A list recording details of all primary ranges included in this class sorted by ascending * address range. */ private LinkedList primaryEntries; /** - * an index identifying primary ranges which have already been encountered. + * An index identifying primary ranges which have already been encountered. */ private Map primaryIndex; /** - * an index of all primary and secondary files referenced from this class's compilation unit. + * An index of all primary and secondary files referenced from this class's compilation unit. */ private Map localFilesIndex; /** @@ -63,42 +77,23 @@ public class ClassEntry { */ private LinkedList localFiles; /** - * an index of all primary and secondary dirs referenced from this class's compilation unit. + * An index of all primary and secondary dirs referenced from this class's compilation unit. */ private HashMap localDirsIndex; /** - * a list of the same dirs. + * A list of the same dirs. */ private LinkedList localDirs; - /** - * index of debug_info section compilation unit for this class. - */ - private int cuIndex; - /** - * index of debug_info section compilation unit for deopt target methods. - */ - private int deoptCUIndex; - /** - * index into debug_line section for associated compilation unit. - */ - private int lineIndex; - /** - * size of line number info prologue region for associated compilation unit. - */ - private int linePrologueSize; - /** - * total size of line number info region for associated compilation unit. - */ - private int totalSize; - /** * true iff the entry includes methods that are deopt targets. */ private boolean includesDeoptTarget; - public ClassEntry(String className, FileEntry fileEntry) { - this.className = className; + public ClassEntry(String className, FileEntry fileEntry, int size) { + super(className, size); + this.interfaces = new LinkedList<>(); this.fileEntry = fileEntry; + this.methods = new LinkedList<>(); this.primaryEntries = new LinkedList<>(); this.primaryIndex = new HashMap<>(); this.localFiles = new LinkedList<>(); @@ -114,15 +109,9 @@ public ClassEntry(String className, FileEntry fileEntry) { localDirsIndex.put(dirEntry, localDirs.size()); } } - this.cuIndex = -1; - this.deoptCUIndex = -1; - this.lineIndex = -1; - this.linePrologueSize = -1; - this.totalSize = -1; - this.includesDeoptTarget = false; } - public void addPrimary(Range primary, List frameSizeInfos, int frameSize) { + public void indexPrimary(Range primary, List frameSizeInfos, int frameSize) { if (primaryIndex.get(primary) == null) { PrimaryEntry primaryEntry = new PrimaryEntry(primary, frameSizeInfos, frameSize, this); primaryEntries.add(primaryEntry); @@ -133,10 +122,13 @@ public void addPrimary(Range primary, List frameSizeInfos, /* deopt targets should all come after normal methods */ assert includesDeoptTarget == false; } + FileEntry fileEntry = primary.getFileEntry(); + assert fileEntry != null; + indexFileEntry(fileEntry); } } - public void addSubRange(Range subrange, FileEntry subFileEntry) { + public void indexSubRange(Range subrange) { Range primary = subrange.getPrimary(); /* * the subrange should belong to a primary range @@ -148,13 +140,18 @@ public void addSubRange(Range subrange, FileEntry subFileEntry) { */ assert primaryEntry != null; assert primaryEntry.getClassEntry() == this; - primaryEntry.addSubRange(subrange, subFileEntry); + primaryEntry.addSubRange(subrange); + FileEntry subFileEntry = subrange.getFileEntry(); if (subFileEntry != null) { - if (localFilesIndex.get(subFileEntry) == null) { - localFiles.add(subFileEntry); - localFilesIndex.put(subFileEntry, localFiles.size()); - } - DirEntry dirEntry = subFileEntry.getDirEntry(); + indexFileEntry(subFileEntry); + } + } + + private void indexFileEntry(FileEntry fileEntry) { + if (localFilesIndex.get(fileEntry) == null) { + localFiles.add(fileEntry); + localFilesIndex.put(fileEntry, localFiles.size()); + DirEntry dirEntry = fileEntry.getDirEntry(); if (dirEntry != null && localDirsIndex.get(dirEntry) == null) { localDirs.add(dirEntry); localDirsIndex.put(dirEntry, localDirs.size()); @@ -200,89 +197,147 @@ String getDirName() { } } - public void setCUIndex(int cuIndex) { - // Should only get set once to a non-negative value. - assert cuIndex >= 0; - assert this.cuIndex == -1; - this.cuIndex = cuIndex; - } - - public int getCUIndex() { - // Should have been set before being read. - assert cuIndex >= 0; - return cuIndex; - } - - public void setDeoptCUIndex(int deoptCUIndex) { - // Should only get set once to a non-negative value. - assert deoptCUIndex >= 0; - assert this.deoptCUIndex == -1; - this.deoptCUIndex = deoptCUIndex; + public FileEntry getFileEntry() { + return fileEntry; } - public int getDeoptCUIndex() { - // Should have been set before being read. - assert deoptCUIndex >= 0; - return deoptCUIndex; + public LinkedList getPrimaryEntries() { + return primaryEntries; } - public int getLineIndex() { - return lineIndex; + public Object primaryIndexFor(Range primaryRange) { + return primaryIndex.get(primaryRange); } - public void setLineIndex(int lineIndex) { - this.lineIndex = lineIndex; + public LinkedList getLocalDirs() { + return localDirs; } - public void setLinePrologueSize(int linePrologueSize) { - this.linePrologueSize = linePrologueSize; + public LinkedList getLocalFiles() { + return localFiles; } - public int getLinePrologueSize() { - return linePrologueSize; + public boolean includesDeoptTarget() { + return includesDeoptTarget; } - public int getTotalSize() { - return totalSize; + public String getCachePath() { + if (fileEntry != null) { + Path cachePath = fileEntry.getCachePath(); + if (cachePath != null) { + return cachePath.toString(); + } + } + return ""; } - public void setTotalSize(int totalSize) { - this.totalSize = totalSize; + @Override + public DebugTypeKind typeKind() { + return DebugTypeKind.INSTANCE; } - public FileEntry getFileEntry() { - return fileEntry; + @Override + public void addDebugInfo(DebugInfoBase debugInfoBase, DebugTypeInfo debugTypeInfo, DebugContext debugContext) { + assert TypeEntry.canonicalize(debugTypeInfo.typeName()).equals(typeName); + DebugInstanceTypeInfo debugInstanceTypeInfo = (DebugInstanceTypeInfo) debugTypeInfo; + /* Add details of super and interface classes */ + String superName = debugInstanceTypeInfo.superName(); + if (superName != null) { + superName = TypeEntry.canonicalize(superName); + } + debugContext.log("typename %s adding super %s\n", typeName, superName); + if (superName != null) { + this.superClass = debugInfoBase.lookupClassEntry(superName); + } + debugInstanceTypeInfo.interfaces().forEach(interfaceName -> processInterface(interfaceName, debugInfoBase, debugContext)); + /* 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.processMethod(methodFieldInfo, debugInfoBase, debugContext)); } - public String getClassName() { - return className; + private void processInterface(String interfaceName, DebugInfoBase debugInfoBase, DebugContext debugContext) { + debugContext.log("typename %s adding interface %s\n", typeName, interfaceName); + ClassEntry entry = debugInfoBase.lookupClassEntry(TypeEntry.canonicalize(interfaceName)); + assert entry instanceof InterfaceClassEntry; + InterfaceClassEntry interfaceClassEntry = (InterfaceClassEntry) entry; + interfaces.add(interfaceClassEntry); + interfaceClassEntry.addImplementor(this, debugContext); } - public LinkedList getPrimaryEntries() { - return primaryEntries; + protected void processMethod(DebugMethodInfo debugMethodInfo, DebugInfoBase debugInfoBase, DebugContext debugContext) { + String methodName = debugInfoBase.uniqueDebugString(debugMethodInfo.name()); + String resultTypeName = TypeEntry.canonicalize(debugMethodInfo.valueType()); + int modifiers = debugMethodInfo.modifiers(); + List paramTypes = debugMethodInfo.paramTypes(); + List paramNames = debugMethodInfo.paramNames(); + assert paramTypes.size() == paramNames.size(); + int paramCount = paramTypes.size(); + debugContext.log("typename %s adding %s method %s %s(%s)\n", + typeName, memberModifiers(modifiers), resultTypeName, methodName, formatParams(paramTypes, paramNames)); + TypeEntry resultType = debugInfoBase.lookupTypeEntry(resultTypeName); + TypeEntry[] paramTypeArray = new TypeEntry[paramCount]; + String[] paramNameArray = new String[paramCount]; + int idx = 0; + for (String paramTypeName : paramTypes) { + TypeEntry paramType = debugInfoBase.lookupTypeEntry(TypeEntry.canonicalize(paramTypeName)); + paramTypeArray[idx++] = paramType; + } + paramNameArray = paramNames.toArray(paramNameArray); + String fileName = debugMethodInfo.fileName(); + Path filePath = debugMethodInfo.filePath(); + Path cachePath = debugMethodInfo.cachePath(); + // n.b. the method file may differ from the owning class file when the method is a + // substitution + FileEntry fileEntry = debugInfoBase.ensureFileEntry(fileName, filePath, cachePath); + methods.add(new MethodEntry(fileEntry, methodName, this, resultType, paramTypeArray, paramNameArray, modifiers)); } - public Object primaryIndexFor(Range primaryRange) { - return primaryIndex.get(primaryRange); - } + private String formatParams(List paramTypes, List paramNames) { + if (paramNames.size() == 0) { + return ""; + } + StringBuilder builder = new StringBuilder(); + String separator = ""; + for (int i = 0; i < paramNames.size(); i++) { + builder.append(separator); + builder.append(paramTypes.get(i)); + String paramName = paramNames.get(i); + if (paramName.length() > 0) { + builder.append(' '); + builder.append(paramName); + } + separator = ", "; + } - public LinkedList getLocalDirs() { - return localDirs; + return builder.toString(); } - public LinkedList getLocalFiles() { - return localFiles; + public boolean isPrimary() { + return primaryEntries.size() != 0; } - public boolean includesDeoptTarget() { - return includesDeoptTarget; + public ClassEntry getSuperClass() { + return superClass; } - public String getCachePath() { - if (fileEntry != null) { - return fileEntry.getCachePath(); - } else { - return ""; + public Range makePrimaryRange(String methodName, String symbolName, String paramSignature, String returnTypeName, StringTable stringTable, FileEntry fileEntry, int lo, int hi, int primaryLine, + int modifiers, boolean isDeoptTarget) { + if (fileEntry == null) { + // search for a matching method to supply the file entry + // or failing that use the one from this class + for (MethodEntry methodEntry : methods) { + if (methodEntry.match(methodName, paramSignature, returnTypeName, isDeoptTarget)) { + // maybe the method's file entry + fileEntry = methodEntry.getFileEntry(); + break; + } + } + if (fileEntry == null) { + // last chance is the class's file entry + fileEntry = this.fileEntry; + } } + return new Range(this.typeName, methodName, symbolName, paramSignature, returnTypeName, stringTable, fileEntry, lo, hi, primaryLine, modifiers, isDeoptTarget); } } 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 f4a3d563839b..362111eda522 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 @@ -32,9 +32,9 @@ import java.nio.ByteOrder; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.HashMap; import java.util.LinkedList; -import java.util.List; import java.util.Map; /** @@ -84,6 +84,14 @@ public abstract class DebugInfoBase { * e.g. enabling all classes in a file to share a single copy of the file and dir tables. */ + /** + * List of class entries detailing class info for primary ranges. + */ + private LinkedList types = new LinkedList<>(); + /** + * index of already seen classes. + */ + private Map typesIndex = new HashMap<>(); /** * List of class entries detailing class info for primary ranges. */ @@ -100,9 +108,15 @@ public abstract class DebugInfoBase { * List of of files which contain primary or secondary ranges. */ private LinkedList files = new LinkedList<>(); + /** + * Flag set to true if heap references are stored as addresses relative to a heap base register + * otherwise false. + */ + private boolean useHeapBase; public DebugInfoBase(ByteOrder byteOrder) { this.byteOrder = byteOrder; + this.useHeapBase = true; } /** @@ -119,16 +133,38 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { * (DebugTypeInfo debugTypeInfo : typeInfoProvider) { install types } */ + /* + * track whether we need to use a heap base regsiter + */ + useHeapBase = debugInfoProvider.useHeapBase(); + /* * Ensure we have a null string in the string section. */ stringTable.uniqueDebugString(""); + // create all the types + debugInfoProvider.typeInfoProvider().forEach(debugTypeInfo -> debugTypeInfo.debugContext((debugContext) -> { + String typeName = TypeEntry.canonicalize(debugTypeInfo.typeName()); + typeName = stringTable.uniqueDebugString(typeName); + DebugTypeKind typeKind = debugTypeInfo.typeKind(); + int byteSize = debugTypeInfo.size(); + + debugContext.log(DebugContext.INFO_LEVEL, "Register %s type %s ", typeKind.toString(), typeName); + String fileName = debugTypeInfo.fileName(); + Path filePath = debugTypeInfo.filePath(); + Path cachePath = debugTypeInfo.cachePath(); + addTypeEntry(typeName, fileName, filePath, cachePath, byteSize, typeKind); + })); + + // now we can cross reference static and instance field details debugInfoProvider.typeInfoProvider().forEach(debugTypeInfo -> debugTypeInfo.debugContext((debugContext) -> { - String typeName = debugTypeInfo.typeName().replaceAll("\\$", "."); + String typeName = TypeEntry.canonicalize(debugTypeInfo.typeName()); DebugTypeKind typeKind = debugTypeInfo.typeKind(); - debugContext.log(DebugContext.INFO_LEVEL, "%s type %s ", typeKind.toString(), typeName); + debugContext.log(DebugContext.INFO_LEVEL, "Process %s type %s ", typeKind.toString(), typeName); + TypeEntry typeEntry = lookupTypeEntry(typeName); + typeEntry.addDebugInfo(this, debugTypeInfo, debugContext); })); debugInfoProvider.codeInfoProvider().forEach(debugCodeInfo -> debugCodeInfo.debugContext((debugContext) -> { @@ -138,25 +174,27 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { String fileName = debugCodeInfo.fileName(); Path filePath = debugCodeInfo.filePath(); Path cachePath = debugCodeInfo.cachePath(); - // switch '$' in class names for '.' - String className = debugCodeInfo.className().replaceAll("\\$", "."); + String className = TypeEntry.canonicalize(debugCodeInfo.className()); String methodName = debugCodeInfo.methodName(); String symbolName = debugCodeInfo.symbolNameForMethod(); - String paramNames = debugCodeInfo.paramNames(); - String returnTypeName = debugCodeInfo.returnTypeName(); + String paramSignature = debugCodeInfo.paramSignature(); + String returnTypeName = TypeEntry.canonicalize(debugCodeInfo.returnTypeName()); int lo = debugCodeInfo.addressLo(); int hi = debugCodeInfo.addressHi(); int primaryLine = debugCodeInfo.line(); boolean isDeoptTarget = debugCodeInfo.isDeoptTarget(); + int modifiers = debugCodeInfo.getModifiers(); - Range primaryRange = new Range(fileName, filePath, cachePath, className, methodName, symbolName, paramNames, returnTypeName, stringTable, lo, hi, primaryLine, isDeoptTarget); + // search for a method defining this primary range + ClassEntry classEntry = ensureClassEntry(className); + FileEntry fileEntry = ensureFileEntry(fileName, filePath, cachePath); + Range primaryRange = classEntry.makePrimaryRange(methodName, symbolName, paramSignature, returnTypeName, stringTable, fileEntry, lo, hi, primaryLine, modifiers, isDeoptTarget); debugContext.log(DebugContext.INFO_LEVEL, "PrimaryRange %s.%s %s %s:%d [0x%x, 0x%x]", className, methodName, filePath, fileName, primaryLine, lo, hi); - addRange(primaryRange, debugCodeInfo.getFrameSizeChanges(), debugCodeInfo.getFrameSize()); + classEntry.indexPrimary(primaryRange, debugCodeInfo.getFrameSizeChanges(), debugCodeInfo.getFrameSize()); debugCodeInfo.lineInfoProvider().forEach(debugLineInfo -> { String fileNameAtLine = debugLineInfo.fileName(); Path filePathAtLine = debugLineInfo.filePath(); - // Switch '$' in class names for '.' - String classNameAtLine = debugLineInfo.className().replaceAll("\\$", "."); + String classNameAtLine = TypeEntry.canonicalize(debugLineInfo.className()); String methodNameAtLine = debugLineInfo.methodName(); String symbolNameAtLine = debugLineInfo.symbolNameForMethod(); int loAtLine = lo + debugLineInfo.addressLo(); @@ -165,93 +203,154 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { Path cachePathAtLine = debugLineInfo.cachePath(); /* * Record all subranges even if they have no line or file so we at least get a - * symbol for them. + * symbol for them and don't see a break in the address range. */ - Range subRange = new Range(fileNameAtLine, filePathAtLine, cachePathAtLine, classNameAtLine, methodNameAtLine, symbolNameAtLine, "", "", stringTable, loAtLine, hiAtLine, line, - primaryRange); - addSubRange(primaryRange, subRange); + FileEntry subFileEntry = ensureFileEntry(fileNameAtLine, filePathAtLine, cachePathAtLine); + Range subRange = new Range(classNameAtLine, methodNameAtLine, symbolNameAtLine, stringTable, subFileEntry, loAtLine, hiAtLine, line, primaryRange); + 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); } }); })); - /* - * This will be needed once we add support for data info: - * - * DebugDataInfoProvider dataInfoProvider = debugInfoProvider.dataInfoProvider(); for - * (DebugDataInfo debugDataInfo : dataInfoProvider) { install details of heap elements - * String name = debugDataInfo.toString(); } - */ + + debugInfoProvider.dataInfoProvider().forEach(debugDataInfo -> debugDataInfo.debugContext((debugContext) -> { + String provenance = debugDataInfo.getProvenance(); + String typeName = debugDataInfo.getTypeName(); + String partitionName = debugDataInfo.getPartition(); + // address is heap-register relative pointer + long address = debugDataInfo.getAddress(); + long size = debugDataInfo.getSize(); + debugContext.log(DebugContext.INFO_LEVEL, "Data: address 0x%x size 0x%x type %s partition %s provenance %s ", address, size, typeName, partitionName, provenance); + })); + } + + private TypeEntry createTypeEntry(String typeName, String fileName, Path filePath, Path cachePath, int size, DebugTypeKind typeKind) { + TypeEntry typeEntry = null; + switch (typeKind) { + case INSTANCE: { + FileEntry fileEntry = addFileEntry(fileName, filePath, cachePath); + typeEntry = new ClassEntry(typeName, fileEntry, size); + break; + } + case INTERFACE: { + FileEntry fileEntry = addFileEntry(fileName, filePath, cachePath); + typeEntry = new InterfaceClassEntry(typeName, fileEntry, size); + break; + } + case ENUM: { + FileEntry fileEntry = addFileEntry(fileName, filePath, cachePath); + typeEntry = new EnumClassEntry(typeName, fileEntry, size); + break; + } + case PRIMITIVE: + assert fileName.length() == 0; + assert filePath == null; + typeEntry = new PrimitiveTypeEntry(typeName, size); + break; + case ARRAY: + assert fileName.length() == 0; + assert filePath == null; + typeEntry = new ArrayTypeEntry(typeName, size); + break; + case HEADER: + assert fileName.length() == 0; + assert filePath == null; + typeEntry = new HeaderTypeEntry(typeName, size); + break; + } + return typeEntry; + } + + private TypeEntry addTypeEntry(String typeName, String fileName, Path filePath, Path cachePath, int size, DebugTypeKind typeKind) { + TypeEntry typeEntry = typesIndex.get(typeName); + if (typeEntry == null) { + typeEntry = createTypeEntry(typeName, fileName, filePath, cachePath, size, typeKind); + types.add(typeEntry); + typesIndex.put(typeName, typeEntry); + } else { + if (!(typeEntry.isClass())) { + assert ((ClassEntry) typeEntry).getFileName().equals(fileName); + } + } + return typeEntry; } - private ClassEntry ensureClassEntry(Range range) { - String className = range.getClassName(); + public TypeEntry lookupTypeEntry(String typeName) { + TypeEntry typeEntry = typesIndex.get(typeName); + if (typeEntry == null) { + throw new RuntimeException("type entry not found " + typeName); + } + return typeEntry; + } + + ClassEntry lookupClassEntry(String typeName) { + TypeEntry typeEntry = typesIndex.get(typeName); + if (typeEntry == null || !(typeEntry.isClass())) { + throw new RuntimeException("class entry not found " + typeName); + } + return (ClassEntry) typeEntry; + } + + private ClassEntry ensureClassEntry(String className) { /* * See if we already have an entry. */ ClassEntry classEntry = primaryClassesIndex.get(className); if (classEntry == null) { - /* - * Create and index the entry associating it with the right file. - */ - FileEntry fileEntry = ensureFileEntry(range); - classEntry = new ClassEntry(className, fileEntry); + TypeEntry typeEntry = typesIndex.get(className); + assert (typeEntry != null && typeEntry.isClass()); + classEntry = (ClassEntry) typeEntry; primaryClasses.add(classEntry); primaryClassesIndex.put(className, classEntry); } - assert classEntry.getClassName().equals(className); + assert (classEntry.getTypeName().equals(className)); return classEntry; } - private FileEntry ensureFileEntry(Range range) { - String fileName = range.getFileName(); - if (fileName == null) { - return null; + private FileEntry addFileEntry(String fileName, Path filePath, Path cachePath) { + assert fileName != null; + Path fileAsPath; + if (filePath != null) { + fileAsPath = filePath.resolve(fileName); + } else { + fileAsPath = Paths.get(fileName); } - Path filePath = range.getFilePath(); - Path fileAsPath = range.getFileAsPath(); - /* - * Ensure we have an entry. - */ FileEntry fileEntry = filesIndex.get(fileAsPath); if (fileEntry == null) { DirEntry dirEntry = ensureDirEntry(filePath); - fileEntry = new FileEntry(fileName, dirEntry, range.getCachePath()); + // ensure file and cachepath are added to the debug_str section + uniqueDebugString(fileName); + uniqueDebugString(cachePath.toString()); + fileEntry = new FileEntry(fileName, dirEntry, cachePath); files.add(fileEntry); /* * Index the file entry by file path. */ filesIndex.put(fileAsPath, fileEntry); - if (!range.isPrimary()) { - /* Check we have a file for the corresponding primary range. */ - Range primaryRange = range.getPrimary(); - FileEntry primaryFileEntry = filesIndex.get(primaryRange.getFileAsPath()); - assert primaryFileEntry != null; - } + } else { + assert (filePath == null || + fileEntry.getDirEntry().getPath().equals(filePath)); } return fileEntry; } - private void addRange(Range primaryRange, List frameSizeInfos, int frameSize) { - assert primaryRange.isPrimary(); - ClassEntry classEntry = ensureClassEntry(primaryRange); - classEntry.addPrimary(primaryRange, frameSizeInfos, frameSize); - } - - private void addSubRange(Range primaryRange, Range subrange) { - assert primaryRange.isPrimary(); - assert !subrange.isPrimary(); - String className = primaryRange.getClassName(); - ClassEntry classEntry = primaryClassesIndex.get(className); - FileEntry subrangeFileEntry = ensureFileEntry(subrange); - /* - * The primary range should already have been seen and associated with a primary class - * entry. - */ - assert classEntry.primaryIndexFor(primaryRange) != null; - if (subrangeFileEntry != null) { - classEntry.addSubRange(subrange, subrangeFileEntry); + protected FileEntry ensureFileEntry(String fileName, Path filePath, Path cachePath) { + if (fileName == null || fileName.length() == 0) { + return null; } + Path fileAsPath; + if (filePath == null) { + fileAsPath = Paths.get(fileName); + } else { + fileAsPath = filePath.resolve(fileName); + } + // Reuse any existing entry + FileEntry fileEntry = findFile(fileAsPath); + if (fileEntry == null) { + fileEntry = addFileEntry(fileName, filePath, cachePath); + } + return fileEntry; } private DirEntry ensureDirEntry(Path filePath) { @@ -260,6 +359,8 @@ private DirEntry ensureDirEntry(Path filePath) { } DirEntry dirEntry = dirsIndex.get(filePath); if (dirEntry == null) { + // ensure dir path is entered into the debug_str section + uniqueDebugString(filePath.toString()); dirEntry = new DirEntry(filePath); dirsIndex.put(filePath, dirEntry); } @@ -271,6 +372,10 @@ public ByteOrder getByteOrder() { return byteOrder; } + public LinkedList getTypes() { + return types; + } + public LinkedList getPrimaryClasses() { return primaryClasses; } @@ -289,6 +394,15 @@ public StringTable getStringTable() { return stringTable; } + /** + * Indirects this call to the string table. + * + * @param string the string whose index is required. + */ + public String uniqueDebugString(String string) { + return stringTable.uniqueDebugString(string); + } + /** * Indirects this call to the string table. * @@ -299,4 +413,8 @@ public StringTable getStringTable() { public int debugStringIndex(String string) { return stringTable.debugStringIndex(string); } + + public boolean useHeapBase() { + return useHeapBase; + } } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DirEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DirEntry.java index 034653171103..4b31fb754d62 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DirEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/DirEntry.java @@ -31,7 +31,7 @@ /** * Tracks the directory associated with one or more source files. * - * This is identified separately from each FileEntry idenityfing files that reside in the directory. + * This is identified separately from each FileEntry identifying files that reside in the directory. * That is necessary because the line info generator needs to collect and write out directory names * into directory tables once only rather than once per file. */ diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/EnumClassEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/EnumClassEntry.java new file mode 100644 index 000000000000..3cebd79da3c0 --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/EnumClassEntry.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2020, Red Hat Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.objectfile.debugentry; + +import com.oracle.objectfile.debuginfo.DebugInfoProvider; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugEnumTypeInfo; +import org.graalvm.compiler.debug.DebugContext; + +public class EnumClassEntry extends ClassEntry { + public EnumClassEntry(String typeName, FileEntry fileEntry, int size) { + super(typeName, fileEntry, size); + } + + @Override + public void addDebugInfo(DebugInfoBase debugInfoBase, DebugInfoProvider.DebugTypeInfo debugTypeInfo, DebugContext debugContext) { + assert debugTypeInfo instanceof DebugEnumTypeInfo; + super.addDebugInfo(debugInfoBase, debugTypeInfo, debugContext); + } +} diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/FieldEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/FieldEntry.java new file mode 100644 index 000000000000..586bf82b58b8 --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/FieldEntry.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2020, Red Hat Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.objectfile.debugentry; + +public class FieldEntry extends MemberEntry { + private final int size; + private final int offset; + + public FieldEntry(FileEntry fileEntry, String fieldName, StructureTypeEntry ownerType, TypeEntry valueType, int size, int offset, int modifiers) { + super(fileEntry, fieldName, ownerType, valueType, modifiers); + this.size = size; + this.offset = offset; + } + + public int getSize() { + return size; + } + + public int getOffset() { + return offset; + } +} diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/FileEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/FileEntry.java index f81eadc6a30c..b55b0544c939 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/FileEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/FileEntry.java @@ -26,15 +26,17 @@ package com.oracle.objectfile.debugentry; +import java.nio.file.Path; + /** * Tracks debug info associated with a Java source file. */ public class FileEntry { private String fileName; private DirEntry dirEntry; - private String cachePath; + private Path cachePath; - public FileEntry(String fileName, DirEntry dirEntry, String cachePath) { + public FileEntry(String fileName, DirEntry dirEntry, Path cachePath) { this.fileName = fileName; this.dirEntry = dirEntry; this.cachePath = cachePath; @@ -48,11 +50,21 @@ public String getFileName() { } public String getPathName() { - return getDirEntry().getPathString(); + DirEntry dirEntry = getDirEntry(); + if (dirEntry == null) { + return ""; + } else { + return dirEntry.getPathString(); + } } public String getFullName() { - return getDirEntry() != null ? getDirEntry().getPath().resolve(getFileName()).toString() : getFileName(); + DirEntry dirEntry = getDirEntry(); + if (dirEntry == null) { + return fileName; + } else { + return dirEntry.getPath().resolve(getFileName()).toString(); + } } /** @@ -65,7 +77,7 @@ public DirEntry getDirEntry() { /** * The compilation directory in which to look for source files as a {@link String}. */ - public String getCachePath() { + public Path getCachePath() { return cachePath; } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/HeaderTypeEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/HeaderTypeEntry.java new file mode 100644 index 000000000000..6d3ff469f802 --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/HeaderTypeEntry.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2020, Red Hat Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.objectfile.debugentry; + +import org.graalvm.compiler.debug.DebugContext; + +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugHeaderTypeInfo; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugTypeInfo; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugTypeInfo.DebugTypeKind; + +public class HeaderTypeEntry extends StructureTypeEntry { + + public HeaderTypeEntry(String typeName, int size) { + super(typeName, size); + } + + @Override + public DebugTypeKind typeKind() { + return DebugTypeKind.HEADER; + } + + @Override + public void addDebugInfo(DebugInfoBase debugInfoBase, DebugTypeInfo debugTypeInfo, DebugContext debugContext) { + assert TypeEntry.canonicalize(debugTypeInfo.typeName()).equals(typeName); + DebugHeaderTypeInfo debugHeaderTypeInfo = (DebugHeaderTypeInfo) debugTypeInfo; + debugHeaderTypeInfo.fieldInfoProvider().forEach(debugFieldInfo -> this.processField(debugFieldInfo, debugInfoBase, debugContext)); + } +} diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/InterfaceClassEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/InterfaceClassEntry.java new file mode 100644 index 000000000000..3b8975a31bba --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/InterfaceClassEntry.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2020, Red Hat Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.objectfile.debugentry; + +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugInterfaceTypeInfo; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugTypeInfo; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugTypeInfo.DebugTypeKind; +import org.graalvm.compiler.debug.DebugContext; + +import java.util.LinkedList; +import java.util.List; +import java.util.stream.Stream; + +public class InterfaceClassEntry extends ClassEntry { + private List implementors; + + public InterfaceClassEntry(String className, FileEntry fileEntry, int size) { + super(className, fileEntry, size); + implementors = new LinkedList<>(); + } + + @Override + public DebugTypeKind typeKind() { + return DebugTypeKind.INTERFACE; + } + + @Override + public void addDebugInfo(DebugInfoBase debugInfoBase, DebugTypeInfo debugTypeInfo, DebugContext debugContext) { + assert debugTypeInfo instanceof DebugInterfaceTypeInfo; + super.addDebugInfo(debugInfoBase, debugTypeInfo, debugContext); + } + + public void addImplementor(ClassEntry classEntry, DebugContext debugContext) { + implementors.add(classEntry); + debugContext.log("typename %s add implementor %s\n", typeName, classEntry.getTypeName()); + } + + public Stream implementors() { + return implementors.stream(); + } +} diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/MemberEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/MemberEntry.java new file mode 100644 index 000000000000..7fad5b085d01 --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/MemberEntry.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2020, Red Hat Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.objectfile.debugentry; + +/** + * An abstract class providing modelling a generic class member which includes behaviour and data + * shared by both field and method entries. + */ +public abstract class MemberEntry { + protected FileEntry fileEntry; + protected final String memberName; + protected final StructureTypeEntry ownerType; + protected final TypeEntry valueType; + protected final int modifiers; + + public MemberEntry(FileEntry fileEntry, String memberName, StructureTypeEntry ownerType, TypeEntry valueType, int modifiers) { + this.fileEntry = fileEntry; + this.memberName = memberName; + this.ownerType = ownerType; + this.valueType = valueType; + this.modifiers = modifiers; + } + + public String getFileName() { + if (fileEntry != null) { + return fileEntry.getFileName(); + } else { + return ""; + } + } + + @SuppressWarnings("unused") + String getFullFileName() { + if (fileEntry != null) { + return fileEntry.getFullName(); + } else { + return null; + } + } + + @SuppressWarnings("unused") + String getDirName() { + if (fileEntry != null) { + return fileEntry.getPathName(); + } else { + return ""; + } + } + + public FileEntry getFileEntry() { + return fileEntry; + } + + public String fieldName() { + return memberName; + } + + public StructureTypeEntry ownerType() { + return ownerType; + } + + public TypeEntry getValueType() { + return valueType; + } + + public int getModifiers() { + return modifiers; + } + + public String getModifiersString() { + return ownerType.memberModifiers(modifiers); + } +} 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 new file mode 100644 index 000000000000..5712b6ec2714 --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/MethodEntry.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2020, Red Hat Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.objectfile.debugentry; + +public class MethodEntry extends MemberEntry { + TypeEntry[] paramTypes; + String[] paramNames; + + public MethodEntry(FileEntry fileEntry, String methodName, ClassEntry ownerType, TypeEntry valueType, TypeEntry[] paramTypes, String[] paramNames, int modifiers) { + 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; + } + + public String methodName() { + return memberName; + } + + @Override + public ClassEntry ownerType() { + assert ownerType instanceof ClassEntry; + return (ClassEntry) ownerType; + } + + public int getParamCount() { + return (paramTypes == null ? 0 : paramTypes.length); + } + + public TypeEntry getParamType(int idx) { + assert paramTypes != null; + assert idx < paramTypes.length; + return paramTypes[idx]; + } + + public String getParamTypeName(int idx) { + assert paramTypes != null; + assert idx < paramTypes.length; + assert paramTypes[idx] != null; + return paramTypes[idx].getTypeName(); + } + + public String getParamName(int idx) { + assert paramNames != null; + assert idx < paramNames.length; + // n.b. param names may be null + return paramNames[idx]; + } + + public boolean match(String methodName, String paramSignature, String returnTypeName, boolean isDeoptTarget) { + if (!methodName.equals(this.memberName)) { + return false; + } + if (!returnTypeName.equals(valueType.getTypeName())) { + return false; + } + int paramCount = getParamCount(); + if (paramCount == 0) { + return paramSignature.trim().length() == 0; + } + String[] paramTypeNames = paramSignature.split((",")); + if (paramCount != paramTypeNames.length) { + return false; + } + for (int i = 0; i < paramCount; i++) { + if (!paramTypeNames[i].trim().equals(getParamTypeName(i))) { + return false; + } + } + return true; + } +} 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 1948cf019ba3..5cd1d2d4f9c7 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 @@ -65,22 +65,19 @@ public PrimaryEntry(Range primary, List frameSizeInfos, in this.primary = primary; this.classEntry = classEntry; this.subranges = new LinkedList<>(); - this.subrangeIndex = new HashMap<>(); this.frameSizeInfos = frameSizeInfos; this.frameSize = frameSize; } - public void addSubRange(Range subrange, FileEntry subFileEntry) { + public void addSubRange(Range subrange) { /* * We should not see a subrange more than once. */ assert !subranges.contains(subrange); - assert subrangeIndex.get(subrange) == null; /* * We need to generate a file table entry for all ranges. */ subranges.add(subrange); - subrangeIndex.put(subrange, subFileEntry); } public Range getPrimary() { @@ -95,10 +92,6 @@ public List getSubranges() { return subranges; } - public FileEntry getSubrangeFileEntry(Range subrange) { - return subrangeIndex.get(subrange); - } - public List getFrameSizeInfos() { return frameSizeInfos; } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/PrimitiveTypeEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/PrimitiveTypeEntry.java new file mode 100644 index 000000000000..0967a2059000 --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/PrimitiveTypeEntry.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2020, Red Hat Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.objectfile.debugentry; + +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugPrimitiveTypeInfo; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugTypeInfo; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugTypeInfo.DebugTypeKind; +import org.graalvm.compiler.debug.DebugContext; + +import static com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugPrimitiveTypeInfo.FLAG_INTEGRAL; +import static com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugPrimitiveTypeInfo.FLAG_NUMERIC; +import static com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugPrimitiveTypeInfo.FLAG_SIGNED; + +public class PrimitiveTypeEntry extends TypeEntry { + char typeChar; + int flags; + int bitCount; + + public PrimitiveTypeEntry(String typeName, int size) { + super(typeName, size); + typeChar = '#'; + flags = 0; + bitCount = 0; + } + + @Override + public DebugTypeKind typeKind() { + return DebugTypeKind.PRIMITIVE; + } + + @Override + public void addDebugInfo(DebugInfoBase debugInfoBase, DebugTypeInfo debugTypeInfo, DebugContext debugContext) { + DebugPrimitiveTypeInfo debugPrimitiveTypeInfo = (DebugPrimitiveTypeInfo) debugTypeInfo; + flags = debugPrimitiveTypeInfo.flags(); + typeChar = debugPrimitiveTypeInfo.typeChar(); + bitCount = debugPrimitiveTypeInfo.bitCount(); + debugContext.log("typename %s %s (%d bits)\n", typeName, decodeFlags(), bitCount); + } + + private String decodeFlags() { + StringBuilder builder = new StringBuilder(); + if ((flags & FLAG_NUMERIC) != 0) { + if ((flags & FLAG_INTEGRAL) != 0) { + if ((flags & FLAG_SIGNED) != 0) { + builder.append("SIGNED "); + } else { + builder.append("UNSIGNED "); + } + builder.append("INTEGRAL"); + } else { + builder.append("FLOATING"); + } + } else { + if (bitCount > 0) { + builder.append("LOGICAL"); + } else { + builder.append("VOID"); + } + } + return builder.toString(); + } + + public char getTypeChar() { + return typeChar; + } + + public int getBitCount() { + return bitCount; + } + + public int getFlags() { + return flags; + } +} 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 d8166f2ddfad..6cdd3ed052aa 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,9 +26,6 @@ package com.oracle.objectfile.debugentry; -import java.nio.file.Path; -import java.nio.file.Paths; - /** * 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 @@ -36,22 +33,22 @@ */ public class Range { - private static final String CLASS_DELIMITER = "."; - - private final String cachePath; - private String fileName; - private Path filePath; + private FileEntry fileEntry; private String className; private String methodName; private String symbolName; - private String paramNames; + private String paramSignature; private String returnTypeName; + private String methodNameWithParams; private String fullMethodName; + private String fullMethodNameWithParams; + private String fullMethodNameWithParamsAndReturnType; private int lo; private int hi; private int line; private boolean isDeoptTarget; + private int modifiers; /* * This is null for a primary range. */ @@ -60,41 +57,42 @@ public class Range { /* * Create a primary range. */ - public Range(String fileName, Path filePath, Path cachePath, String className, String methodName, String symbolName, String paramNames, String returnTypeName, StringTable stringTable, int lo, - int hi, int line, boolean isDeoptTarget) { - this(fileName, filePath, cachePath, className, methodName, symbolName, paramNames, returnTypeName, stringTable, lo, hi, line, isDeoptTarget, null); + public Range(String className, String methodName, String symbolName, String paramSignature, String returnTypeName, StringTable stringTable, FileEntry fileEntry, int lo, int hi, int line, + int modifiers, boolean isDeoptTarget) { + this(className, methodName, symbolName, paramSignature, returnTypeName, stringTable, fileEntry, lo, hi, line, modifiers, isDeoptTarget, null); } /* * Create a secondary range. */ - public Range(String fileName, Path filePath, Path cachePath, String className, String methodName, String symbolName, String paramNames, String returnTypeName, StringTable stringTable, int lo, - int hi, int line, Range primary) { - this(fileName, filePath, cachePath, className, methodName, symbolName, paramNames, returnTypeName, stringTable, lo, hi, line, false, primary); + public Range(String className, String methodName, String symbolName, StringTable stringTable, FileEntry fileEntry, int lo, int hi, int line, + Range primary) { + this(className, methodName, symbolName, "", "", stringTable, fileEntry, lo, hi, line, 0, false, primary); } /* * Create a primary or secondary range. */ - private Range(String fileName, Path filePath, Path cachePath, String className, String methodName, String symbolName, String paramNames, String returnTypeName, StringTable stringTable, int lo, - int hi, int line, boolean isDeoptTarget, Range primary) { - /* - * Currently file name and full method name need to go into the debug_str section other - * strings just need to be deduplicated to save space. - */ - this.fileName = (fileName == null ? null : stringTable.uniqueDebugString(fileName)); - this.filePath = filePath; - this.cachePath = (cachePath == null ? "" : stringTable.uniqueDebugString(cachePath.toString())); + private Range(String className, String methodName, String symbolName, String paramSignature, String returnTypeName, StringTable stringTable, FileEntry fileEntry, int lo, int hi, int line, + int modifiers, boolean isDeoptTarget, Range primary) { + this.fileEntry = fileEntry; + if (fileEntry != null) { + stringTable.uniqueDebugString(fileEntry.getFileName()); + stringTable.uniqueDebugString(fileEntry.getPathName()); + } this.className = stringTable.uniqueString(className); this.methodName = stringTable.uniqueString(methodName); this.symbolName = stringTable.uniqueString(symbolName); - this.paramNames = stringTable.uniqueString(paramNames); + this.paramSignature = stringTable.uniqueString(paramSignature); this.returnTypeName = stringTable.uniqueString(returnTypeName); - this.fullMethodName = stringTable.uniqueDebugString(constructClassAndMethodNameWithParams()); + this.fullMethodName = stringTable.uniqueString(constructClassAndMethodName()); + this.fullMethodNameWithParams = stringTable.uniqueString(constructClassAndMethodNameWithParams()); + this.fullMethodNameWithParamsAndReturnType = stringTable.uniqueString(constructClassAndMethodNameWithParamsAndReturnType()); this.lo = lo; this.hi = hi; this.line = line; this.isDeoptTarget = isDeoptTarget; + this.modifiers = modifiers; this.primary = primary; } @@ -110,24 +108,6 @@ public Range getPrimary() { return primary; } - public String getFileName() { - return fileName; - } - - public Path getFilePath() { - return filePath; - } - - public Path getFileAsPath() { - if (filePath != null) { - return filePath.resolve(fileName); - } else if (fileName != null) { - return Paths.get(fileName); - } else { - return null; - } - } - public String getClassName() { return className; } @@ -156,50 +136,79 @@ public String getFullMethodName() { return fullMethodName; } - public boolean isDeoptTarget() { - return isDeoptTarget; + public String getFullMethodNameWithParams() { + return fullMethodNameWithParams; } - public String getParamNames() { - return paramNames; + public String getFullMethodNameWithParamsAndReturnType() { + return fullMethodNameWithParamsAndReturnType; } - public String getClassAndMethodName() { - return getExtendedMethodName(false, false); + public boolean isDeoptTarget() { + return isDeoptTarget; } - private String getExtendedMethodName(boolean includeParams, boolean includeReturnType) { + private String getExtendedMethodName(boolean includeClass, boolean includeParams, boolean includeReturnType) { StringBuilder builder = new StringBuilder(); if (includeReturnType && returnTypeName.length() > 0) { builder.append(returnTypeName); builder.append(' '); } - if (className != null) { + if (includeClass && className != null) { builder.append(className); builder.append(CLASS_DELIMITER); } builder.append(methodName); - if (includeParams && !paramNames.isEmpty()) { + if (includeParams) { builder.append('('); - builder.append(paramNames); + builder.append(paramSignature); builder.append(')'); } + if (includeReturnType) { + builder.append(" "); + builder.append(returnTypeName); + } return builder.toString(); } + private String constructClassAndMethodName() { + return getExtendedMethodName(true, false, false); + } + private String constructClassAndMethodNameWithParams() { - return getExtendedMethodName(true, false); + return getExtendedMethodName(true, true, false); } - /** - * Get the compilation directory in which to look for source files as a {@link String}. - */ - public String getCachePath() { - return cachePath; + private String constructClassAndMethodNameWithParamsAndReturnType() { + return getExtendedMethodName(true, true, true); + } + + public String getMethodReturnTypeName() { + return returnTypeName; + } + + public String getParamSignature() { + return paramSignature; + } + + public FileEntry getFileEntry() { + return fileEntry; + } + + public void setFileEntry(FileEntry fileEntry) { + this.fileEntry = fileEntry; + } + + public int getModifiers() { + return modifiers; } @Override public String toString() { - return String.format("Range(lo=0x%05x hi=0x%05x %s %s:%d)", lo, hi, constructClassAndMethodNameWithParams(), getFileAsPath(), line); + return String.format("Range(lo=0x%05x hi=0x%05x %s %s:%d)", lo, hi, constructClassAndMethodNameWithParams(), fileEntry.getFullName(), line); + } + + public String getFileName() { + return fileEntry.getFileName(); } } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/StringEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/StringEntry.java index c6d077661f64..b984a63e02d1 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/StringEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/StringEntry.java @@ -45,10 +45,7 @@ public String getString() { } public int getOffset() { - /* - * Offset must be set before this can be fetched - */ - assert offset >= 0; + assert offset >= -1; return offset; } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/StructureTypeEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/StructureTypeEntry.java new file mode 100644 index 000000000000..057fcabb4c1d --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/StructureTypeEntry.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2020, Red Hat Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.objectfile.debugentry; + +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFieldInfo; +import org.graalvm.compiler.debug.DebugContext; + +import java.lang.reflect.Modifier; +import java.nio.file.Path; +import java.util.LinkedList; +import java.util.List; +import java.util.stream.Stream; + +/** + * An intermediate type that provides behaviour for managing fields. This unifies code for handling + * header structures and Java instance classes that both support data members. + */ +public abstract class StructureTypeEntry extends TypeEntry { + /** + * Details of fields located in this instance. + */ + protected List fields; + + public StructureTypeEntry(String typeName, int size) { + super(typeName, size); + this.fields = new LinkedList<>(); + } + + public Stream fields() { + return fields.stream(); + } + + protected void processField(DebugFieldInfo debugFieldInfo, DebugInfoBase debugInfoBase, DebugContext debugContext) { + String fieldName = debugInfoBase.uniqueDebugString(debugFieldInfo.name()); + String valueTypeName = TypeEntry.canonicalize(debugFieldInfo.valueType()); + int size = debugFieldInfo.size(); + int offset = debugFieldInfo.offset(); + int modifiers = debugFieldInfo.modifiers(); + debugContext.log("typename %s adding %s field %s type %s size %s at offset %d\n", + typeName, memberModifiers(modifiers), fieldName, valueTypeName, size, offset); + TypeEntry valueType = debugInfoBase.lookupTypeEntry(valueTypeName); + String fileName = debugFieldInfo.fileName(); + Path filePath = debugFieldInfo.filePath(); + Path cachePath = debugFieldInfo.cachePath(); + // n.b. the field file may differ from the owning class file when the field is a + // substitution + FileEntry fileEntry = debugInfoBase.ensureFileEntry(fileName, filePath, cachePath); + fields.add(new FieldEntry(fileEntry, fieldName, this, valueType, size, offset, modifiers)); + } + + String memberModifiers(int modifiers) { + StringBuilder builder = new StringBuilder(); + if (Modifier.isPublic(modifiers)) { + builder.append("public "); + } else if (Modifier.isProtected(modifiers)) { + builder.append("protected "); + } else if (Modifier.isPrivate(modifiers)) { + builder.append("private "); + } + if (Modifier.isFinal(modifiers)) { + builder.append("final "); + } + if (Modifier.isAbstract(modifiers)) { + builder.append("abstract "); + } else if (Modifier.isVolatile(modifiers)) { + builder.append("volatile "); + } else if (Modifier.isTransient(modifiers)) { + builder.append("transient "); + } else if (Modifier.isSynchronized(modifiers)) { + builder.append("synchronized "); + } + if (Modifier.isNative(modifiers)) { + builder.append("native "); + } + if (Modifier.isStatic(modifiers)) { + builder.append("static"); + } else { + builder.append("instance"); + } + + return builder.toString(); + } +} diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/TypeEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/TypeEntry.java new file mode 100644 index 000000000000..e74032fe450a --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/TypeEntry.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2020, Red Hat Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.objectfile.debugentry; + +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugTypeInfo; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugTypeInfo.DebugTypeKind; +import org.graalvm.compiler.debug.DebugContext; + +import static com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugTypeInfo.DebugTypeKind.ARRAY; +import static com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugTypeInfo.DebugTypeKind.ENUM; +import static com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugTypeInfo.DebugTypeKind.HEADER; +import static com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugTypeInfo.DebugTypeKind.INSTANCE; +import static com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugTypeInfo.DebugTypeKind.INTERFACE; +import static com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugTypeInfo.DebugTypeKind.PRIMITIVE; + +public abstract class TypeEntry { + /** + * The name of this type. + */ + protected String typeName; + + /** + * The size of an occurrence of this type in bytes. + */ + protected int size; + + protected TypeEntry(String typeName, int size) { + this.typeName = typeName; + this.size = size; + } + + public int getSize() { + return size; + } + + public String getTypeName() { + return typeName; + } + + public abstract DebugTypeKind typeKind(); + + public boolean isPrimitive() { + return typeKind() == PRIMITIVE; + } + + public boolean isHeader() { + return typeKind() == HEADER; + } + + public boolean isArray() { + return typeKind() == ARRAY; + } + + public boolean isInstance() { + return typeKind() == INSTANCE; + } + + public boolean isInterface() { + return typeKind() == INTERFACE; + } + + public boolean isEnum() { + return typeKind() == ENUM; + } + + public boolean isClass() { + return isInstance() | isInterface() || isEnum(); + } + + public boolean isStructure() { + return isClass() || isHeader(); + } + + public abstract void addDebugInfo(DebugInfoBase debugInfoBase, DebugTypeInfo debugTypeInfo, DebugContext debugContext); + + public static String canonicalize(String typeName) { + // typeName = typeName.replace('$', '.'); + typeName = typeName.replace(" ", "__"); + return typeName; + } +} 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 2d358288845f..3c95d7697d12 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 @@ -38,16 +38,38 @@ * underlying object file so that the latter can insert appropriate debug info. */ public interface DebugInfoProvider { + boolean useHeapBase(); + /** - * Access details of a specific type. + * An interface implemented by items that can be located in a file. */ - interface DebugTypeInfo { + interface DebugFileInfo { + /** + * @return the name of the file containing a file element excluding any path. + */ + String fileName(); + + /** + * @return a relative path to the file containing a file element derived from its package + * name or {@code null} if the element is in the empty package. + */ + Path filePath(); + + /** + * @return a relative path to the source cache containing the cached source file of a file + * element or {@code null} if sources are not available. + */ + Path cachePath(); + } + + interface DebugTypeInfo extends DebugFileInfo { enum DebugTypeKind { PRIMITIVE, ENUM, INSTANCE, INTERFACE, - ARRAY; + ARRAY, + HEADER; @Override public String toString() { @@ -62,6 +84,8 @@ public String toString() { return "interface"; case ARRAY: return "array"; + case HEADER: + return "header"; default: return "???"; } @@ -80,73 +104,87 @@ public String toString() { int size(); } - interface DebugEnumTypeInfo extends DebugInstanceTypeInfo { - } - interface DebugInstanceTypeInfo extends DebugTypeInfo { int headerSize(); + Stream fieldInfoProvider(); + + Stream methodInfoProvider(); + + String superName(); + + Stream interfaces(); } - interface DebugInterfaceTypeInfo extends DebugTypeInfo { + interface DebugEnumTypeInfo extends DebugInstanceTypeInfo { + } + + interface DebugInterfaceTypeInfo extends DebugInstanceTypeInfo { } interface DebugArrayTypeInfo extends DebugTypeInfo { int headerSize(); + int lengthOffset(); + String elementType(); } interface DebugPrimitiveTypeInfo extends DebugTypeInfo { /* - * NUMERIC excludes boolean and void + * NUMERIC excludes LOGICAL types boolean and void */ int FLAG_NUMERIC = 1 << 0; /* - * INTEGRAL excludes float and double + * INTEGRAL excludes FLOATING types float and double */ int FLAG_INTEGRAL = 1 << 1; /* - * SIGNED excludes char + * SIGNED excludes UNSIGNED type char */ int FLAG_SIGNED = 1 << 2; + int bitCount(); + + char typeChar(); + int flags(); } - interface DebugFieldInfo { + interface DebugHeaderTypeInfo extends DebugTypeInfo { + + Stream fieldInfoProvider(); + } + + interface DebugMemberInfo extends DebugFileInfo { + + String name(); + String ownerType(); String valueType(); + int modifiers(); + } + + interface DebugFieldInfo extends DebugMemberInfo { int offset(); int size(); } + interface DebugMethodInfo extends DebugMemberInfo { + List paramTypes(); + + List paramNames(); + } + /** * Access details of a specific compiled method. */ - interface DebugCodeInfo { + interface DebugCodeInfo extends DebugFileInfo { void debugContext(Consumer action); - /** - * @return the name of the file containing a compiled method excluding any path. - */ - String fileName(); - - /** - * @return a relative path to the file containing a compiled method derived from its package - * name or null if the method is in the empty package. - */ - Path filePath(); - - /** - * @return a relative path to the source cache containing the sources of a compiled method - * or {@code null} if sources are not available. - */ - Path cachePath(); - /** * @return the fully qualified name of the class owning the compiled method. */ @@ -188,7 +226,7 @@ interface DebugCodeInfo { /** * @return a string identifying the method parameters. */ - String paramNames(); + String paramSignature(); /** * @return a string identifying the method return type. @@ -210,36 +248,34 @@ interface DebugCodeInfo { * @return true if this method has been compiled in as a deoptimization target */ boolean isDeoptTarget(); + + int getModifiers(); } /** * Access details of a specific heap object. */ interface DebugDataInfo { + void debugContext(Consumer action); + + String getProvenance(); + + String getTypeName(); + + String getPartition(); + + long getOffset(); + + long getAddress(); + + long getSize(); } /** * Access details of code generated for a specific outer or inlined method at a given line * number. */ - interface DebugLineInfo { - /** - * @return the name of the file containing the outer or inlined method excluding any path. - */ - String fileName(); - - /** - * @return a relative path to the file containing the outer or inlined method derived from - * its package name or null if the method is in the empty package. - */ - Path filePath(); - - /** - * @return a relative path to the source cache containing the sources of a compiled method - * or {@code null} if sources are not available. - */ - Path cachePath(); - + interface DebugLineInfo extends DebugFileInfo { /** * @return the fully qualified name of the class owning the outer or inlined method. */ diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfARangesSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfARangesSectionImpl.java index d09ca707b699..b7eae8bd1e6b 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfARangesSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfARangesSectionImpl.java @@ -38,7 +38,7 @@ import java.util.Map; import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ARANGES_SECTION_NAME; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_INFO_SECTION_NAME; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_FRAME_SECTION_NAME; import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_VERSION_2; /** @@ -59,6 +59,8 @@ public String getSectionName() { @Override public void createContent() { + assert !contentByteArrayCreated(); + int pos = 0; /* * We need an entry for each compilation unit. @@ -156,6 +158,7 @@ public byte[] getOrDecideContent(Map alre @Override public void writeContent(DebugContext context) { + assert contentByteArrayCreated(); byte[] buffer = getContent(); int size = buffer.length; int pos = 0; @@ -166,7 +169,7 @@ public void writeContent(DebugContext context) { for (ClassEntry classEntry : getPrimaryClasses()) { int lastpos = pos; int length = DW_AR_HEADER_SIZE + DW_AR_HEADER_PAD_SIZE - 4; - int cuIndex = classEntry.getCUIndex(); + int cuIndex = getCUIndex(classEntry); LinkedList classPrimaryEntries = classEntry.getPrimaryEntries(); /* * Count only real methods, omitting deopt targets. @@ -204,7 +207,7 @@ public void writeContent(DebugContext context) { * Emit only real methods, omitting linkage stubs. */ if (!primary.isDeoptTarget()) { - log(context, " [0x%08x] %016x %016x %s", pos, debugTextBase + primary.getLo(), primary.getHi() - primary.getLo(), primary.getFullMethodName()); + log(context, " [0x%08x] %016x %016x %s", pos, debugTextBase + primary.getLo(), primary.getHi() - primary.getLo(), primary.getFullMethodNameWithParams()); pos = putRelocatableCodeOffset(primary.getLo(), buffer, pos); pos = putLong(primary.getHi() - primary.getLo(), buffer, pos); } @@ -217,7 +220,7 @@ public void writeContent(DebugContext context) { if (classEntry.includesDeoptTarget()) { int lastpos = pos; int length = DW_AR_HEADER_SIZE + DW_AR_HEADER_PAD_SIZE - 4; - int cuIndex = classEntry.getDeoptCUIndex(); + int cuIndex = getDeoptCUIndex(classEntry); LinkedList classPrimaryEntries = classEntry.getPrimaryEntries(); /* * Count only linkage stubs. @@ -257,7 +260,7 @@ public void writeContent(DebugContext context) { * Emit only linkage stubs. */ if (primary.isDeoptTarget()) { - log(context, " [0x%08x] %016x %016x %s", pos, debugTextBase + primary.getLo(), primary.getHi() - primary.getLo(), primary.getFullMethodName()); + log(context, " [0x%08x] %016x %016x %s", pos, debugTextBase + primary.getLo(), primary.getHi() - primary.getLo(), primary.getFullMethodNameWithParams()); pos = putRelocatableCodeOffset(primary.getLo(), buffer, pos); pos = putLong(primary.getHi() - primary.getLo(), buffer, pos); } @@ -270,9 +273,9 @@ public void writeContent(DebugContext context) { } /* - * The debug_aranges section content depends on debug_info section content and offset. + * The debug_aranges section depends on debug_frame section. */ - private static final String TARGET_SECTION_NAME = DW_INFO_SECTION_NAME; + private static final String TARGET_SECTION_NAME = DW_FRAME_SECTION_NAME; @Override public String targetSectionName() { @@ -281,7 +284,7 @@ public String targetSectionName() { private final LayoutDecision.Kind[] targetSectionKinds = { LayoutDecision.Kind.CONTENT, - LayoutDecision.Kind.OFFSET + LayoutDecision.Kind.SIZE }; @Override 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 da017aae9749..243450a16108 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 @@ -29,29 +29,85 @@ import com.oracle.objectfile.LayoutDecision; import org.graalvm.compiler.debug.DebugContext; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_compile_unit_1; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_compile_unit_2; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_subprogram; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_array_data_type; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_array_layout; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_array_pointer; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_array_typedef; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_array_unit; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_builtin_unit; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_class_layout; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_class_pointer; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_class_typedef; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_class_unit1; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_class_unit2; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_field_declaration1; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_field_declaration2; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_field_declaration3; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_field_declaration4; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_header_field; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_interface_implementor; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_interface_layout; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_interface_pointer; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_interface_typedef; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_method_declaration1; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_method_declaration2; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_method_location; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_method_parameter_declaration1; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_method_parameter_declaration2; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_method_parameter_declaration3; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_object_header; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_primitive_type; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_static_field_location; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_super_reference; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_void_type; import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_SECTION_NAME; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ARANGES_SECTION_NAME; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_accessibility; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_artificial; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_bit_size; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_byte_size; import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_comp_dir; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_containing_type; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_data_member_location; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_decl_file; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_declaration; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_encoding; import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_external; import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_hi_pc; import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_language; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_location; import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_low_pc; import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_name; import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_null; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_specification; import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_stmt_list; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_type; import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_CHILDREN_no; import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_CHILDREN_yes; import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_FORM_addr; import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_FORM_data1; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_FORM_data2; import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_FORM_data4; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_FORM_expr_loc; import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_FORM_flag; import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_FORM_null; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_FORM_ref_addr; import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_FORM_strp; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_FRAME_SECTION_NAME; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_TAG_array_type; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_TAG_base_type; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_TAG_class_type; import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_TAG_compile_unit; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_TAG_formal_parameter; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_TAG_inheritance; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_TAG_member; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_TAG_pointer_type; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_TAG_structure_type; import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_TAG_subprogram; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_TAG_typedef; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_TAG_union_type; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_TAG_unspecified_type; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_TAG_variable; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.Dw_AT_object_pointer; /** * Section generator for debug_abbrev section. @@ -69,13 +125,13 @@ public String getSectionName() { @Override public void createContent() { - int pos = 0; + assert !contentByteArrayCreated(); /* * An abbrev table contains abbrev entries for one or more CUs. the table includes a * sequence of abbrev entries each of which defines a specific DIE layout employed to * describe some DIE in a CU. a table is terminated by a null entry. * - * A null entry has consists of just a 0 abbrev code. + * A null entry consists of just a 0 abbrev code. * *
    * @@ -83,7 +139,7 @@ public void createContent() { * *
* - * Mon-null entries have the following format. + * Non-null entries have the following format. * *
    * @@ -97,7 +153,9 @@ public void createContent() { * *
  • attribute_spec* .......... zero or more attributes * - *
  • null_attribute_spec ...... terminator
+ *
  • null_attribute_spec ...... terminator + * + * * * An attribute_spec consists of an attribute name and form * @@ -109,41 +167,521 @@ public void createContent() { * * * - * For the moment we only use one abbrev table for all CUs. It contains three DIEs. The - * first two describe the compilation unit itself and the third describes each method within - * that compilation unit. + * For the moment we only use one abbrev table for all CUs. It contains the following DIES: + * + *
      + * + *
    • Level 0 DIEs + * + *
    • code = null, TAG = null - empty terminator + * + *
    • code = builtin_unit, TAG = compile_unit - Java primitive and header type + * compile unit + * + *
    • code = class_unit1/2, tag = compile_unit - Java instance type compile + * unit + * + *
    • code = array_unit, tag = compile_unit - Java array type compile unit + * + *
    + * + *
      + * + *
    • Level 1 DIES + * + *
    • code = primitive_type, tag = base_type - Java primitive type (non-void) + * + *
    • code = void_type, tag = unspecified_type - Java void type + * + *
    • code = object_header, tag = structure_type - Java object header + * + *
    • code = class_layout, tag = class_type, parent = class_unit - Java + * instance type structure definition + * + *
    • code = class_pointer, tag = pointer_type, parent = class_unit - Java + * instance ref type + * + *
    • code = class_typedef, tag = typedef, parent = class_unit - Java instance + * ref typedef + * + *
    • code = method_location, tag = subprogram , parent = class_unit - Java + * method code definition (i.e. location of code) + * + *
    • code = static_field_location, tag = variable, parent = class_unit - Java + * static field definition (i.e. location of data) + * + *
    • code = array_layout, tag = structure_type, parent = array_unit - Java + * array type structure definition + * + *
    • code = array_pointer, tag = pointer_type, parent = array_unit - Java + * array ref type + * + *
    • code = array_typedef, tag = typedef, parent = array_unit - Java array ref + * typedef + * + *
    • code = interface_layout, tag = union_type, parent = class_unit - Java + * array type structure definition + * + *
    • code = interface_pointer, tag = pointer_type, parent = class_unit - Java + * interface ref type + * + *
    • code = interface_typedef, tag = typedef, parent = class_unit - Java array + * ref typedef + * + *
    + * + *
      + * + *
    • Level 2 DIEs + * + *
    • code = header_field, tag = member, parent = object_header - object/array + * header field + * + *
    • code == method_declaration1/2, tag == subprogram, parent = class_layout + * + *
    • code = field_declaration1/2/3/4, tag = member, parent = + * object_header/class_layout - object header or instance field declaration (i.e. + * specification of properties) + * + *
    • code == super_reference, tag == inheritance, parent = + * class_layout/array_layout - reference to super class layout or to appropriate + * header struct for {code java.lang.Object} or arrays. + * + *
    • code == interface_implementor, tag == member, parent = interface_layout + * - union member typed using class layout of a given implementing class + * + *
    + * + *
  • Level 2/3 DIEs + * + *
  • code == method_parameter_declaration1/2/3, tag == formal_parameter, parent = + * method_declaration1/2, method_location - details of method parameters + * + * Details of each specific DIE contents are as follows: + * + * Primitive Types: For each non-void Java primitive type there is a level 0 DIE defining a + * base type + * + *
      + * + *
    • abbrev_code == primitive_type, tag == DW_TAG_base_type, no_children + * + *
    • DW_AT_byte_size : ... DW_FORM_data1 (or data2 ???) + * + *
    • DW_AT_bit_size : ... DW_FORM_data1 (or data2 ???) + * + *
    • DW_AT_encoding : .... DW_FORM_data1 + * + *
    • DW_AT_name : ........ DW_FORM_strp + * + *
    + * + *
      + * + * The void type is defined as an unspecified type + * + *
    • abbrev_code == void_type, tag == DW_TAG_unspecified_type, no_children + * + *
    • DW_AT_name : ........ DW_FORM_strp + * + *
    + * + * Header: There is a level 0 DIE defining structure types used to define the various types + * of header structure embedded at the start of every instance or array. All instances embed + * the same object header. Array headers embed the object header as a parent type, allowing + * an array to be viewed as a type of object. Multiple array headers structures are defined + * to allow for the possibility of different amounts of padding required between the array + * header fields and the array elements that are allocate at the end of the header. Child + * DIEs are employed to define the name, type and layout of fields in each header. + * + *
      + * + *
    • abbrev_code == object_header, tag == DW_TAG_structure_type, has_children + * + *
    • DW_AT_name : ......... DW_FORM_strp "oop" + * + *
    • DW_AT_byte_size : ... DW_FORM_data1 "oop" + * + *
    + * + * Header Data: A level 1 DIE of type member is used to describe the fields of both object + * and array headers. This includes the type tag and other tag bits in all objects, the + * length field in all arrays and any padding bytes needed to complete the layout. + * + *
      + * + *
    • abbrev_code = header_field, tag == DW_TAG_member, no_children + * + *
    • Dw_AT_name : ................... DW_FORM_strp + * + *
    • Dw_AT_type : ................... DW_FORM_ref_addr + * + *
    • Dw_AT_data_member_location : ... DW_FORM_data1 + * + *
    • Dw_AT_accessibility : .......... DW_FORM_data1 + * + *
    + * + * Instance Classes: For each class there is a level 0 DIE defining the class compilation + * unit * - * The DIE layouts are as follows: + *
      * - *
      • abbrev_code == 1 or 2, tag == DW_TAG_compilation_unit, has_children + *
      • abbrev_code == class_unit1/2, tag == DW_TAG_compilation_unit, + * has_children * *
      • DW_AT_language : ... DW_FORM_data1 * *
      • DW_AT_name : ....... DW_FORM_strp * - *
      • DW_AT_low_pc : ..... DW_FORM_address + *
      • DW_AT_comp_dir : ... DW_FORM_strp * - *
      • DW_AT_hi_pc : ...... DW_FORM_address + *
      • DW_AT_low_pc : ..... DW_FORM_address ??? omit this so method entries do + * not need to occupy full range + * + *
      • DW_AT_hi_pc : ...... DW_FORM_address ??? omit this so method entries do + * not need to occupy full range * *
      • DW_AT_stmt_list : .. DW_FORM_data4 n.b only for abbrev-code == - * 2 + * class_unit1 * *
      * - *
      • abbrev_code == 3, tag == DW_TAG_subprogram, no_children + * Instance Class Structure: Each class_unit DIE contains a series of level 1 DIEs. The + * first one describes the class layout: * - *
      • DW_AT_name : ....... DW_FORM_strp + *
          + * + *
        • abbrev_code == class_layout, tag == DW_TAG_class_type, has_children + * + *
        • Dw_AT_name : ........ DW_FORM_strp + * + *
        • Dw_AT_byte_size : ... DW_FORM_data1/2 ??? how many bytes do we really + * need? + * + *
        • Dw_AT_decl_file : ... DW_FORM_data1/2 ??? how many bytes do we really + * need? + * + *
        • Dw_AT_decl_line : ... DW_FORM_data1/2 ??? how many bytes do we really + * need? + * + *
        • Dw_AT_containing_type : ..... DW_FORM_ref_addr + * + *
        + * + * Instance Class members: The level 1 class_layout DIE includes a level 2 child for each of + * the class's methods and fields. The first type declares a method but omits details of the + * location of the code that implements the method. The second type declares an instance or + * static field. The class_layout DIE also contains an level 2 DIE specifying the type from + * which it inherits superclass structure. In the case of class Object structure is + * inherited from the object header structure type. + * + * n.b. Code implementation details for each method are provided in an auxiliary level 1 + * method_location DIE that follows the class_unit DIE. Instance field declarations need no + * auxiliary level 1 DIE as all relevant details, including size and offset in the instance, + * are specified in the level 2 field declaration DIE. Static field locations are provided + * in an auxiliary level 1 DIE (with tag variable) that follows the class_unit DIE. + * + *
          + * + *
        • abbrev_code == method_declaration1/2, tag == DW_TAG_subprogram, + * has_children + * + *
        • DW_AT_external : .......... DW_FORM_flag ??? for all? + * + *
        • Dw_AT_name : .............. DW_FORM_strp + * + *
        • DW_AT_decl_file : ......... DW_FORM_data1/2 ??? how many bytes + * + *
        • DW_AT_decl_line : ......... DW_FORM_data1/2 ??? how many bytes + * + *
        • Dw_AT_linkage_name : ...... DW_FORM_strp + * + *
        • Dw_AT_type : .............. DW_FORM_ref_addr (optional!!) + * + *
        • DW_AT_artificial : ........ DW_FORM_flag + * + *
        • DW_AT_accessibility : ..... DW_FORM_data1 + * + *
        • DW_AT_declaration : ....... DW_FORM_flag + * + *
        • Dw_AT_object_pointer : .... DW_FORM_ref_addr (only for + * method_declaration1 points to param 0 DIE) + * + *
        • DW_AT_virtuality : ........ DW_FORM_data1 (for override methods) + * + *
        • DW_AT_containing_type : ... DW_FORM_ref_addr (for override methods) + * + *
        + * + *
          + * + *
        • abbrev_code == field_declaration1/2/3/4, tag == DW_TAG_member, + * no_children + * + *
        • Dw_AT_name : ................... DW_FORM_strp + * + *
        • DW_AT_decl_file : .............. DW_FORM_data1/2 (only for + * field_declaration2/4) + * + *
        • DW_AT_decl_line : .............. DW_FORM_data1/2 (only for + * field_declaration2/4) + * + *
        • Dw_AT_type : ................... DW_FORM_ref_addr + * + *
        • Dw_AT_data_member_location : ... DW_FORM_data1/2 (only for + * field_declaration1/2 instance) ??? how many bytes? + * + *
        • Dw_AT_artificial : ............. DW_FORM_flag ?? do we need this? + * + *
        • Dw_AT_accessibility : .......... DW_FORM_data1 + * + *
        • Dw_AT_external : ............... DW_FORM_flag (only for + * field_declaration3/4 static) + * + *
        • Dw_AT_declaration : ............ DW_FORM_flag (only for + * field_declaration3/4 static) + * + *
        + * + *
          + * + *
        • abbrev_code == super_reference, tag == DW_TAG_inheritance, no_children + * + *
        • Dw_AT_type : ................... DW_FORM_ref_addr * - *
        • DW_AT_hi_pc : ...... DW_FORM_addr + *
        • Dw_AT_data_member_location : ... DW_FORM_data1/2 * - *
        • DW_AT_external : ... DW_FORM_flag + *
        • Dw_AT_accessibility :........... DW_FORM_data1 * *
        + * + * Method Parameters: Level 2 method_declaration DIEs may include level 3 DIEs that describe + * their parameters + * + *
          + * + *
        • abbrev_code == method_parameter_declaration1/2/3, tag == + * DW_TAG_formal_parameter, no_children + * + *
        • Dw_AT_name : ... DW_FORM_strp (may be empty string) + * + *
        • Dw_AT_file : ... DW_FORM_data1/2 (optional only for + * method_parameter_declaration2) + * + *
        • Dw_AT_line : ... DW_FORM_data1/2 (optional only for + * method_parameter_declaration2) + * + *
        • Dw_AT_type : ... DW_FORM_ref_addr + * + *
        • Dw_AT_artificial : ... DW_FORM_flag (optional only for + * method_parameter_declaration1 $this, $access) + * + *
        • Dw_AT_location(list???) : ... DW_FORM_exprloc + * + *
        + * + * Instance Class Reference Types: A level 1 class_layout DIE is followed by a DIE defining + * a pointer to the class and a second DIE that defines a typedef for that pointer using the + * Java class name as the typedef name. This reflects the fact that a Java object reference + * is actually implemented as a pointer. + * + * n.b. the name used in the class_layout DIE is not the Java class name. It is derived by + * appending '_' to the Java class name (preceding the package prefix). So this means that, + * for example, the Java type java.lang.Object appears to gdb to be defined as follows + * + * typedef struct _java.lang.Object { ... } *java.lang.Object; + * + *
          + * + *
        • abbrev_code == class_pointer, tag == DW_TAG_pointer_type, no_children + * + *
        • Dw_AT_byte_size : ... DW_FORM_data1 + * + *
        • Dw_AT_type : ........ DW_FORM_ref_addr + * + *
        + * + *
          + * + *
        • abbrev_code == class_typedef, tag == DW_TAG_typedef, no_children + * + *
        • Dw_AT_name : ... DW_FORM_strp + * + *
        • Dw_AT_type : ........ DW_FORM_ref_addr + * + *
        + * + * 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. + * + *
          + * + *
        • abbrev_code == DW_ABBREV_CODE_method_location, tag == DW_TAG_subprogram, + * has_children + * + *
        • DW_AT_low_pc : .......... DW_FORM_addr + * + *
        • DW_AT_hi_pc : ........... DW_FORM_addr (or data8???) + * + *
        • DW_AT_external : ........ DW_FORM_flag + * + *
        • DW_AT_specification : ... DW_FORM_ref_addr + * + *
        + * + * Static Field Locations: For each static field within the class there is a level 1 DIE + * providing details of the static field location + * + *
          + * + *
        • abbrev_code == static_field_location, tag == DW_TAG_variable, + * no_children + * + *
        • DW_AT_specification : ... DW_FORM_ref_addr + * + *
        • DW_AT_linkage_name : .... DW_FORM_strp + * + *
        • DW_AT_location : ........ DW_FORM_exprloc + * + *
        + * + * Arrays: For each array type there is a level 0 DIE defining the array compilation unit + * + *
          + * + *
        • abbrev_code == array_unit, tag == DW_TAG_compilation_unit, has_children + * + *
        • DW_AT_language : ... DW_FORM_data1 + * + *
        • DW_AT_name : ....... DW_FORM_strp ??? what name??? + * + *
        + * + * Array Structure: Each array_unit DIE contains three level 1 DIEs. The first one describes + * the array layout: + * + *
          + * + *
        • abbrev_code == array_layout, tag == DW_TAG_class_type, has_children + * + *
        • Dw_AT_name : ........ DW_FORM_strp + * + *
        • Dw_AT_byte_size : ... DW_FORM_data1/2 size up to base of embedded array + * elements? + * + *
        + * + * The second DIE defines the array reference type as a pointer to the underlying structure + * type + * + *
          + * + *
        • abbrev_code == array_pointer, tag == DW_TAG_pointer_type, no_children + * + *
        • Dw_AT_byte_size : ... DW_FORM_data1 + * + *
        • Dw_AT_type : ........ DW_FORM_ref_addr + * + * The third DIE defines the array type name as a typedef for the pointer type + * + *
            + * + *
          • abbrev_code == array_typedef, tag == DW_TAG_typedef, no_children + * + *
          • Dw_AT_name : ....... DW_FORM_strp + * + *
          • Dw_AT_type : ........ DW_FORM_ref_addr + * + *
          + * + * n.b. the name used in the array_layout DIE is not the Java array name. It is derived by + * appending '_' to the Java array name (preceding the package prefix). So this means that, + * for example, the Java type java.lang.String[] appears to gdb to be defined as follows + * + * typedef struct _java.lang.String[] { ... } *java.lang.String[]; + * + * Array members: The level 1 array_layout DIE includes level 2 child DIEs with tag member + * that describe the layout of the array. header_field DIEs are used to declare members of + * the array header, including the zero length array data member tat dfollows other header + * fields. An auxiliary array_data_type DIE with tag array_type also occurs as a child DIE + * defining the type for the array data member. + * + *
            + * + *
          • abbrev_code == array_data_type, tag == DW_TAG_array_type, no_children + * + *
          • Dw_AT_byte_size : ... DW_FORM_data1 + * + *
          • Dw_AT_type : ........ DW_FORM_ref_addr + * + *
          + * + * Interfaces: For each interface there is a level 0 class_unit DIE defining the interface + * compilation unit. + * + * Interface Layout and Reference Types: The level 0 class_unit DIE for an interface is + * followed by a level 1 DIE defining the interface layout as a union of all the layouts for + * the classes which implement the interface. Two more level 1 DIEs define the a pointer to + * this layout type and a typedef that names the interface pointer type using the Java + * interface name. + * + * n.b. the name used in the interface_layout DIE is not the Java interface name. It is + * derived by appending '_' to the Java class name (preceding the package prefix). So this + * means that, for example, the Java interface java.lang.CharSequence appears to gdb to be + * defined as follows + * + * typedef union _java.lang.CharSequence { ... } *java.lang.CharSequence; + * + *
            + * + *
          • abbrev_code == interface_layout, tag == union_type, has_children + * + *
          • Dw_AT_name : ....... DW_FORM_strp + * + *
          + * + *
            + * + *
          • abbrev_code == interface_pointer, tag == pointer_type, has_children + * + *
          • Dw_AT_byte_size : ... DW_FORM_data1 + * + *
          • DW_AT_TYPE : ....... DW_FORM_ref_addr + * + *
          + * + * The union type embeds level 2 DIEs with tag member. There is a member for each + * implementing class, typed using the layout. + * + *
            + * + *
          • abbrev_code == interface_implementor, tag == member, no_children + * + *
          • Dw_AT_name : ................... DW_FORM_strp + * + *
          • Dw_AT_type : ................... DW_FORM_ref_addr + * + *
          • Dw_AT_accessibility : .......... DW_FORM_data1 + * + *
          + * + * The member name is constructed by appending an '_' to the Java* name of the implementing + * class. So, this means that, for example, the Java interface java.lang.CharSequence will + * include members for String, StringBuffer etc as follows + * + * typedef union _java.lang.CharSequence { _java.lang.String _java.lang.String; + * _java.lang.StringBuffer _java.lang.StringBuffer; ... } *java.lang.CharSequence; + * */ - pos = writeCUAbbrev1(null, null, pos); - pos = writeCUAbbrev2(null, null, pos); - pos = writeMethodAbbrev(null, null, pos); + int pos = 0; + pos = writeAbbrevs(null, null, pos); byte[] buffer = new byte[pos]; super.setContent(buffer); @@ -151,26 +689,46 @@ public void createContent() { @Override public void writeContent(DebugContext context) { + assert contentByteArrayCreated(); + byte[] buffer = getContent(); int size = buffer.length; int pos = 0; enableLog(context, pos); - pos = writeCUAbbrev1(context, buffer, pos); - pos = writeCUAbbrev2(context, buffer, pos); - pos = writeMethodAbbrev(context, buffer, pos); + pos = writeAbbrevs(context, buffer, pos); + assert pos == size; } - @SuppressWarnings("unused") - private int writeCUAbbrev1(DebugContext context, byte[] buffer, int p) { - return writeCUAbbrev(context, DW_ABBREV_CODE_compile_unit_1, buffer, p); - } + public int writeAbbrevs(DebugContext context, byte[] buffer, int pos) { + pos = writeBuiltInUnitAbbrev(context, buffer, pos); + pos = writeClassUnitAbbrevs(context, buffer, pos); + pos = writeArrayUnitAbbrev(context, buffer, pos); + + pos = writePrimitiveTypeAbbrev(context, buffer, pos); + pos = writeVoidTypeAbbrev(context, buffer, pos); + pos = writeObjectHeaderAbbrev(context, buffer, pos); + + pos = writeClassLayoutAbbrev(context, buffer, pos); + pos = writeClassReferenceAbbrev(context, buffer, pos); + pos = writeMethodDeclarationAbbrevs(context, buffer, pos); + pos = writeFieldDeclarationAbbrevs(context, buffer, pos); + pos = writeArrayLayoutAbbrev(context, buffer, pos); + pos = writeArrayReferenceAbbrev(context, buffer, pos); + pos = writeInterfaceLayoutAbbrev(context, buffer, pos); + pos = writeInterfaceReferenceAbbrev(context, buffer, pos); - @SuppressWarnings("unused") - private int writeCUAbbrev2(DebugContext context, byte[] buffer, int p) { - return writeCUAbbrev(context, DW_ABBREV_CODE_compile_unit_2, buffer, p); + pos = writeHeaderFieldAbbrev(context, buffer, pos); + pos = writeArrayDataTypeAbbrev(context, buffer, pos); + pos = writeMethodLocationAbbrev(context, buffer, pos); + pos = writeStaticFieldLocationAbbrev(context, buffer, pos); + pos = writeSuperReferenceAbbrev(context, buffer, pos); + pos = writeInterfaceImplementorAbbrev(context, buffer, pos); + + pos = writeParameterDeclarationAbbrevs(context, buffer, pos); + return pos; } private int writeAttrType(long code, byte[] buffer, int pos) { @@ -189,12 +747,32 @@ private int writeAttrForm(long code, byte[] buffer, int pos) { } } - @SuppressWarnings("unused") - private int writeCUAbbrev(DebugContext context, int abbrevCode, byte[] buffer, int p) { + private int writeBuiltInUnitAbbrev(DebugContext context, byte[] buffer, int p) { + int pos = p; + pos = writeAbbrevCode(DW_ABBREV_CODE_builtin_unit, buffer, pos); + pos = writeTag(DW_TAG_compile_unit, buffer, pos); + pos = writeFlag(DW_CHILDREN_yes, buffer, pos); + pos = writeAttrType(DW_AT_language, buffer, pos); + pos = writeAttrForm(DW_FORM_data1, buffer, pos); + /* + * Now terminate. + */ + pos = writeAttrType(DW_AT_null, buffer, pos); + pos = writeAttrForm(DW_FORM_null, buffer, pos); + return pos; + } + + private int writeClassUnitAbbrevs(DebugContext context, byte[] buffer, int p) { + int pos = p; + // class compile unit no line info + pos = writeClassUnitAbbrev(context, DW_ABBREV_CODE_class_unit1, buffer, pos); + // class compile unit with line info + pos = writeClassUnitAbbrev(context, DW_ABBREV_CODE_class_unit2, buffer, pos); + return pos; + } + + private int writeClassUnitAbbrev(DebugContext context, int abbrevCode, byte[] buffer, int p) { int pos = p; - /* - * Abbrev 1/2 compile unit. - */ pos = writeAbbrevCode(abbrevCode, buffer, pos); pos = writeTag(DW_TAG_compile_unit, buffer, pos); pos = writeFlag(DW_CHILDREN_yes, buffer, pos); @@ -208,7 +786,7 @@ private int writeCUAbbrev(DebugContext context, int abbrevCode, byte[] buffer, i pos = writeAttrForm(DW_FORM_addr, buffer, pos); pos = writeAttrType(DW_AT_hi_pc, buffer, pos); pos = writeAttrForm(DW_FORM_addr, buffer, pos); - if (abbrevCode == DW_ABBREV_CODE_compile_unit_1) { + if (abbrevCode == DW_ABBREV_CODE_class_unit1) { pos = writeAttrType(DW_AT_stmt_list, buffer, pos); pos = writeAttrForm(DW_FORM_data4, buffer, pos); } @@ -220,23 +798,492 @@ private int writeCUAbbrev(DebugContext context, int abbrevCode, byte[] buffer, i return pos; } - @SuppressWarnings("unused") - private int writeMethodAbbrev(DebugContext context, byte[] buffer, int p) { + private int writeArrayUnitAbbrev(DebugContext context, byte[] buffer, int p) { + int pos = p; + + pos = writeAbbrevCode(DW_ABBREV_CODE_array_unit, buffer, pos); + pos = writeTag(DW_TAG_compile_unit, buffer, pos); + pos = writeFlag(DW_CHILDREN_yes, buffer, pos); + pos = writeAttrType(DW_AT_language, buffer, pos); + pos = writeAttrForm(DW_FORM_data1, buffer, pos); + pos = writeAttrType(DW_AT_name, buffer, pos); + pos = writeAttrForm(DW_FORM_strp, buffer, pos); + /* + * Now terminate. + */ + pos = writeAttrType(DW_AT_null, buffer, pos); + pos = writeAttrForm(DW_FORM_null, buffer, pos); + return pos; + } + + private int writePrimitiveTypeAbbrev(DebugContext context, byte[] buffer, int p) { + int pos = p; + pos = writeAbbrevCode(DW_ABBREV_CODE_primitive_type, buffer, pos); + pos = writeTag(DW_TAG_base_type, buffer, pos); + pos = writeFlag(DW_CHILDREN_no, buffer, pos); + pos = writeAttrType(DW_AT_byte_size, buffer, pos); + pos = writeAttrForm(DW_FORM_data1, buffer, pos); + pos = writeAttrType(DW_AT_bit_size, buffer, pos); + pos = writeAttrForm(DW_FORM_data1, buffer, pos); + pos = writeAttrType(DW_AT_encoding, buffer, pos); + pos = writeAttrForm(DW_FORM_data1, buffer, pos); + pos = writeAttrType(DW_AT_name, buffer, pos); + pos = writeAttrForm(DW_FORM_strp, buffer, pos); + /* + * Now terminate. + */ + pos = writeAttrType(DW_AT_null, buffer, pos); + pos = writeAttrForm(DW_FORM_null, buffer, pos); + return pos; + } + + private int writeVoidTypeAbbrev(DebugContext context, byte[] buffer, int p) { + int pos = p; + pos = writeAbbrevCode(DW_ABBREV_CODE_void_type, buffer, pos); + pos = writeTag(DW_TAG_unspecified_type, buffer, pos); + pos = writeFlag(DW_CHILDREN_no, buffer, pos); + pos = writeAttrType(DW_AT_name, buffer, pos); + pos = writeAttrForm(DW_FORM_strp, buffer, pos); + /* + * Now terminate. + */ + pos = writeAttrType(DW_AT_null, buffer, pos); + pos = writeAttrForm(DW_FORM_null, buffer, pos); + return pos; + } + + private int writeObjectHeaderAbbrev(DebugContext context, byte[] buffer, int p) { + int pos = p; + pos = writeAbbrevCode(DW_ABBREV_CODE_object_header, buffer, pos); + pos = writeTag(DW_TAG_structure_type, buffer, pos); + pos = writeFlag(DW_CHILDREN_yes, buffer, pos); + pos = writeAttrType(DW_AT_name, buffer, pos); + pos = writeAttrForm(DW_FORM_strp, buffer, pos); + pos = writeAttrType(DW_AT_byte_size, buffer, pos); + pos = writeAttrForm(DW_FORM_data1, buffer, pos); + /* + * Now terminate. + */ + pos = writeAttrType(DW_AT_null, buffer, pos); + pos = writeAttrForm(DW_FORM_null, buffer, pos); + return pos; + } + + private int writeClassLayoutAbbrev(DebugContext context, byte[] buffer, int p) { + int pos = p; + + pos = writeAbbrevCode(DW_ABBREV_CODE_class_layout, buffer, pos); + pos = writeTag(DW_TAG_class_type, buffer, pos); + pos = writeFlag(DW_CHILDREN_yes, buffer, pos); + pos = writeAttrType(DW_AT_name, buffer, pos); + pos = writeAttrForm(DW_FORM_strp, buffer, pos); + pos = writeAttrType(DW_AT_byte_size, buffer, pos); + pos = writeAttrForm(DW_FORM_data2, buffer, pos); + pos = writeAttrType(DW_AT_decl_file, buffer, pos); + pos = writeAttrForm(DW_FORM_data2, buffer, pos); + // at present we definitely don't have line numbers + // pos = writeAttrType(DW_AT_decl_line, buffer, pos); + // pos = writeAttrForm(DW_FORM_data2, buffer, pos); + // n.b. the containing_type attribute is not strict DWARF but gdb expects it + // we also add an inheritance member with the same info + pos = writeAttrType(DW_AT_containing_type, buffer, pos); + pos = writeAttrForm(DW_FORM_ref_addr, buffer, pos); + /* + * Now terminate. + */ + pos = writeAttrType(DW_AT_null, buffer, pos); + pos = writeAttrForm(DW_FORM_null, buffer, pos); + return pos; + } + + private int writeClassReferenceAbbrev(DebugContext context, byte[] buffer, int p) { int pos = p; + + // first the basic pointer type for a pointer to the class struct type + pos = writeAbbrevCode(DW_ABBREV_CODE_class_pointer, buffer, pos); + pos = writeTag(DW_TAG_pointer_type, buffer, pos); + pos = writeFlag(DW_CHILDREN_no, buffer, pos); + pos = writeAttrType(DW_AT_byte_size, buffer, pos); + pos = writeAttrForm(DW_FORM_data1, buffer, pos); + pos = writeAttrType(DW_AT_type, buffer, pos); + pos = writeAttrForm(DW_FORM_ref_addr, buffer, pos); + /* + * Now terminate. + */ + pos = writeAttrType(DW_AT_null, buffer, pos); + pos = writeAttrForm(DW_FORM_null, buffer, pos); + + // we wll also need a typedef to advertise the class name as the pointer type + pos = writeAbbrevCode(DW_ABBREV_CODE_class_typedef, buffer, pos); + pos = writeTag(DW_TAG_typedef, buffer, pos); + pos = writeFlag(DW_CHILDREN_no, buffer, pos); + pos = writeAttrType(DW_AT_name, buffer, pos); + pos = writeAttrForm(DW_FORM_strp, buffer, pos); + pos = writeAttrType(DW_AT_type, buffer, pos); + pos = writeAttrForm(DW_FORM_ref_addr, buffer, pos); /* - * Abbrev 2 compile unit. + * Now terminate. */ - pos = writeAbbrevCode(DW_ABBREV_CODE_subprogram, buffer, pos); + pos = writeAttrType(DW_AT_null, buffer, pos); + pos = writeAttrForm(DW_FORM_null, buffer, pos); + return pos; + } + + private int writeMethodDeclarationAbbrevs(DebugContext context, byte[] buffer, int p) { + int pos = p; + pos = writeMethodDeclarationAbbrev(context, DW_ABBREV_CODE_method_declaration1, buffer, pos); + pos = writeMethodDeclarationAbbrev(context, DW_ABBREV_CODE_method_declaration2, buffer, pos); + return pos; + } + + private int writeMethodDeclarationAbbrev(DebugContext context, int abbrevCode, byte[] buffer, int p) { + int pos = p; + pos = writeAbbrevCode(abbrevCode, buffer, pos); pos = writeTag(DW_TAG_subprogram, buffer, pos); + pos = writeFlag(DW_CHILDREN_yes, buffer, pos); + pos = writeAttrType(DW_AT_external, buffer, pos); + pos = writeAttrForm(DW_FORM_flag, buffer, pos); + pos = writeAttrType(DW_AT_name, buffer, pos); + pos = writeAttrForm(DW_FORM_strp, buffer, pos); + pos = writeAttrType(DW_AT_decl_file, buffer, pos); + pos = writeAttrForm(DW_FORM_data2, buffer, pos); + // pos = writeAttrType(DW_AT_decl_line, buffer, pos); + // pos = writeAttrForm(DW_FORM_data2, buffer, pos); + // pos = writeAttrType(Dw_AT_linkage_name, buffer, pos); // not in DWARF2 + // pos = writeAttrForm(DW_FORM_strp, buffer, pos); + pos = writeAttrType(DW_AT_type, buffer, pos); + pos = writeAttrForm(DW_FORM_ref_addr, buffer, pos); + pos = writeAttrType(DW_AT_artificial, buffer, pos); + pos = writeAttrForm(DW_FORM_flag, buffer, pos); + pos = writeAttrType(DW_AT_accessibility, buffer, pos); + pos = writeAttrForm(DW_FORM_data1, buffer, pos); + pos = writeAttrType(DW_AT_declaration, buffer, pos); + pos = writeAttrForm(DW_FORM_flag, buffer, pos); + // pos = writeAttrType(DW_AT_virtuality, buffer, pos); + // pos = writeAttrForm(DW_FORM_data1, buffer, pos); + pos = writeAttrType(DW_AT_containing_type, buffer, pos); + pos = writeAttrForm(DW_FORM_ref_addr, buffer, pos); + if (abbrevCode == DW_ABBREV_CODE_method_declaration1) { + pos = writeAttrType(Dw_AT_object_pointer, buffer, pos); + pos = writeAttrForm(DW_FORM_ref_addr, buffer, pos); + } + /* + * Now terminate. + */ + pos = writeAttrType(DW_AT_null, buffer, pos); + pos = writeAttrForm(DW_FORM_null, buffer, pos); + return pos; + } + + private int writeFieldDeclarationAbbrevs(DebugContext context, byte[] buffer, int p) { + int pos = p; + // instance field no line and file + pos = writeFieldDeclarationAbbrev(context, DW_ABBREV_CODE_field_declaration1, buffer, pos); + // instance field with line and file + pos = writeFieldDeclarationAbbrev(context, DW_ABBREV_CODE_field_declaration2, buffer, pos); + // static field no line and file + pos = writeFieldDeclarationAbbrev(context, DW_ABBREV_CODE_field_declaration3, buffer, pos); + // static field with line and file + pos = writeFieldDeclarationAbbrev(context, DW_ABBREV_CODE_field_declaration4, buffer, pos); + return pos; + } + + private int writeFieldDeclarationAbbrev(DebugContext context, int abbrevCode, byte[] buffer, int p) { + int pos = p; + pos = writeAbbrevCode(abbrevCode, buffer, pos); + pos = writeTag(DW_TAG_member, buffer, pos); + pos = writeFlag(DW_CHILDREN_no, buffer, pos); + pos = writeAttrType(DW_AT_name, buffer, pos); + pos = writeAttrForm(DW_FORM_strp, buffer, pos); + // we may not have a file and line for a field + if (abbrevCode == DW_ABBREV_CODE_field_declaration2 || abbrevCode == DW_ABBREV_CODE_field_declaration4) { + pos = writeAttrType(DW_AT_decl_file, buffer, pos); + pos = writeAttrForm(DW_FORM_data2, buffer, pos); + // at present we definitely don't have line numbers + // pos = writeAttrType(DW_AT_decl_line, buffer, pos); + // pos = writeAttrForm(DW_FORM_data2, buffer, pos); + } + pos = writeAttrType(DW_AT_type, buffer, pos); + pos = writeAttrForm(DW_FORM_ref_addr, buffer, pos); + if (abbrevCode == DW_ABBREV_CODE_field_declaration1 || abbrevCode == DW_ABBREV_CODE_field_declaration2) { + // instance fields have a member offset relocated relative to the heap base register + pos = writeAttrType(DW_AT_data_member_location, buffer, pos); + pos = writeAttrForm(DW_FORM_data2, buffer, pos); + } + pos = writeAttrType(DW_AT_accessibility, buffer, pos); + pos = writeAttrForm(DW_FORM_data1, buffer, pos); + // static fields are only declared here and are external + if (abbrevCode == DW_ABBREV_CODE_field_declaration3 || abbrevCode == DW_ABBREV_CODE_field_declaration4) { + pos = writeAttrType(DW_AT_external, buffer, pos); + pos = writeAttrForm(DW_FORM_flag, buffer, pos); + pos = writeAttrType(DW_AT_declaration, buffer, pos); + pos = writeAttrForm(DW_FORM_flag, buffer, pos); + } + /* + * Now terminate. + */ + pos = writeAttrType(DW_AT_null, buffer, pos); + pos = writeAttrForm(DW_FORM_null, buffer, pos); + return pos; + } + + private int writeArrayLayoutAbbrev(DebugContext context, byte[] buffer, int p) { + int pos = p; + + pos = writeAbbrevCode(DW_ABBREV_CODE_array_layout, buffer, pos); + pos = writeTag(DW_TAG_structure_type, buffer, pos); + pos = writeFlag(DW_CHILDREN_yes, buffer, pos); + pos = writeAttrType(DW_AT_name, buffer, pos); + pos = writeAttrForm(DW_FORM_strp, buffer, pos); + pos = writeAttrType(DW_AT_byte_size, buffer, pos); + pos = writeAttrForm(DW_FORM_data2, buffer, pos); + /* + * Now terminate. + */ + pos = writeAttrType(DW_AT_null, buffer, pos); + pos = writeAttrForm(DW_FORM_null, buffer, pos); + return pos; + } + + private int writeArrayReferenceAbbrev(DebugContext context, byte[] buffer, int p) { + int pos = p; + + pos = writeAbbrevCode(DW_ABBREV_CODE_array_pointer, buffer, pos); + pos = writeTag(DW_TAG_pointer_type, buffer, pos); + pos = writeFlag(DW_CHILDREN_no, buffer, pos); + pos = writeAttrType(DW_AT_byte_size, buffer, pos); + pos = writeAttrForm(DW_FORM_data1, buffer, pos); + pos = writeAttrType(DW_AT_type, buffer, pos); + pos = writeAttrForm(DW_FORM_ref_addr, buffer, pos); + /* + * Now terminate. + */ + pos = writeAttrType(DW_AT_null, buffer, pos); + pos = writeAttrForm(DW_FORM_null, buffer, pos); + + // we wll also need a typedef to advertise the array name as the pointer type + pos = writeAbbrevCode(DW_ABBREV_CODE_array_typedef, buffer, pos); + pos = writeTag(DW_TAG_typedef, buffer, pos); + pos = writeFlag(DW_CHILDREN_no, buffer, pos); + pos = writeAttrType(DW_AT_name, buffer, pos); + pos = writeAttrForm(DW_FORM_strp, buffer, pos); + pos = writeAttrType(DW_AT_type, buffer, pos); + pos = writeAttrForm(DW_FORM_ref_addr, buffer, pos); + /* + * Now terminate. + */ + pos = writeAttrType(DW_AT_null, buffer, pos); + pos = writeAttrForm(DW_FORM_null, buffer, pos); + return pos; + } + + private int writeInterfaceLayoutAbbrev(DebugContext context, byte[] buffer, int p) { + int pos = p; + + pos = writeAbbrevCode(DW_ABBREV_CODE_interface_layout, buffer, pos); + pos = writeTag(DW_TAG_union_type, buffer, pos); + pos = writeFlag(DW_CHILDREN_yes, buffer, pos); + pos = writeAttrType(DW_AT_name, buffer, pos); + pos = writeAttrForm(DW_FORM_strp, buffer, pos); + /* + * Now terminate. + */ + pos = writeAttrType(DW_AT_null, buffer, pos); + pos = writeAttrForm(DW_FORM_null, buffer, pos); + return pos; + } + + private int writeInterfaceReferenceAbbrev(DebugContext context, byte[] buffer, int p) { + int pos = p; + + pos = writeAbbrevCode(DW_ABBREV_CODE_interface_pointer, buffer, pos); + pos = writeTag(DW_TAG_pointer_type, buffer, pos); + pos = writeFlag(DW_CHILDREN_no, buffer, pos); + pos = writeAttrType(DW_AT_byte_size, buffer, pos); + pos = writeAttrForm(DW_FORM_data1, buffer, pos); + pos = writeAttrType(DW_AT_type, buffer, pos); + pos = writeAttrForm(DW_FORM_ref_addr, buffer, pos); + /* + * Now terminate. + */ + pos = writeAttrType(DW_AT_null, buffer, pos); + pos = writeAttrForm(DW_FORM_null, buffer, pos); + + // we wll also need a typedef to advertise the interface name as the pointer type + pos = writeAbbrevCode(DW_ABBREV_CODE_interface_typedef, buffer, pos); + pos = writeTag(DW_TAG_typedef, buffer, pos); + pos = writeFlag(DW_CHILDREN_no, buffer, pos); + pos = writeAttrType(DW_AT_name, buffer, pos); + pos = writeAttrForm(DW_FORM_strp, buffer, pos); + pos = writeAttrType(DW_AT_type, buffer, pos); + pos = writeAttrForm(DW_FORM_ref_addr, buffer, pos); + /* + * Now terminate. + */ + pos = writeAttrType(DW_AT_null, buffer, pos); + pos = writeAttrForm(DW_FORM_null, buffer, pos); + return pos; + } + + private int writeInterfaceImplementorAbbrev(DebugContext context, byte[] buffer, int p) { + int pos = p; + + pos = writeAbbrevCode(DW_ABBREV_CODE_interface_implementor, buffer, pos); + pos = writeTag(DW_TAG_member, buffer, pos); + pos = writeFlag(DW_CHILDREN_no, buffer, pos); + pos = writeAttrType(DW_AT_name, buffer, pos); + pos = writeAttrForm(DW_FORM_strp, buffer, pos); + pos = writeAttrType(DW_AT_type, buffer, pos); + pos = writeAttrForm(DW_FORM_ref_addr, buffer, pos); + pos = writeAttrType(DW_AT_accessibility, buffer, pos); + pos = writeAttrForm(DW_FORM_data1, buffer, pos); + /* + * Now terminate. + */ + pos = writeAttrType(DW_AT_null, buffer, pos); + pos = writeAttrForm(DW_FORM_null, buffer, pos); + return pos; + } + + private int writeNarrowLayoutAbbrev(DebugContext context, byte[] buffer, int p) { + int pos = p; + return pos; + } + + private int writeHeaderFieldAbbrev(DebugContext context, byte[] buffer, int p) { + int pos = p; + + pos = writeAbbrevCode(DW_ABBREV_CODE_header_field, buffer, pos); + pos = writeTag(DW_TAG_member, buffer, pos); pos = writeFlag(DW_CHILDREN_no, buffer, pos); pos = writeAttrType(DW_AT_name, buffer, pos); pos = writeAttrForm(DW_FORM_strp, buffer, pos); + pos = writeAttrType(DW_AT_type, buffer, pos); + pos = writeAttrForm(DW_FORM_ref_addr, buffer, pos); + pos = writeAttrType(DW_AT_data_member_location, buffer, pos); + pos = writeAttrForm(DW_FORM_data1, buffer, pos); + pos = writeAttrType(DW_AT_accessibility, buffer, pos); + pos = writeAttrForm(DW_FORM_data1, buffer, pos); + /* + * Now terminate. + */ + pos = writeAttrType(DW_AT_null, buffer, pos); + pos = writeAttrForm(DW_FORM_null, buffer, pos); + return pos; + } + + private int writeArrayDataTypeAbbrev(DebugContext context, byte[] buffer, int p) { + int pos = p; + + pos = writeAbbrevCode(DW_ABBREV_CODE_array_data_type, buffer, pos); + pos = writeTag(DW_TAG_array_type, buffer, pos); + pos = writeFlag(DW_CHILDREN_no, buffer, pos); + pos = writeAttrType(DW_AT_byte_size, buffer, pos); + pos = writeAttrForm(DW_FORM_data1, buffer, pos); + pos = writeAttrType(DW_AT_type, buffer, pos); + pos = writeAttrForm(DW_FORM_ref_addr, buffer, pos); + /* + * Now terminate. + */ + pos = writeAttrType(DW_AT_null, buffer, pos); + pos = writeAttrForm(DW_FORM_null, buffer, pos); + return pos; + } + + private int writeMethodLocationAbbrev(DebugContext context, byte[] buffer, int p) { + int pos = p; + pos = writeAbbrevCode(DW_ABBREV_CODE_method_location, buffer, pos); + pos = writeTag(DW_TAG_subprogram, buffer, pos); + pos = writeFlag(DW_CHILDREN_yes, buffer, pos); pos = writeAttrType(DW_AT_low_pc, buffer, pos); pos = writeAttrForm(DW_FORM_addr, buffer, pos); pos = writeAttrType(DW_AT_hi_pc, buffer, pos); pos = writeAttrForm(DW_FORM_addr, buffer, pos); pos = writeAttrType(DW_AT_external, buffer, pos); pos = writeAttrForm(DW_FORM_flag, buffer, pos); + pos = writeAttrType(DW_AT_specification, buffer, pos); + pos = writeAttrForm(DW_FORM_ref_addr, buffer, pos); + /* + * Now terminate. + */ + pos = writeAttrType(DW_AT_null, buffer, pos); + pos = writeAttrForm(DW_FORM_null, buffer, pos); + return pos; + } + + private int writeStaticFieldLocationAbbrev(DebugContext context, byte[] buffer, int p) { + int pos = p; + + pos = writeAbbrevCode(DW_ABBREV_CODE_static_field_location, buffer, pos); + pos = writeTag(DW_TAG_variable, buffer, pos); + pos = writeFlag(DW_CHILDREN_no, buffer, pos); + pos = writeAttrType(DW_AT_specification, buffer, pos); + pos = writeAttrForm(DW_FORM_ref_addr, buffer, pos); + // pos = writeAttrType(DW_AT_linkage_name, buffer, pos); + // pos = writeAttrForm(DW_FORM_strp, buffer, pos); + pos = writeAttrType(DW_AT_location, buffer, pos); + pos = writeAttrForm(DW_FORM_expr_loc, buffer, pos); + /* + * Now terminate. + */ + pos = writeAttrType(DW_AT_null, buffer, pos); + pos = writeAttrForm(DW_FORM_null, buffer, pos); + return pos; + } + + private int writeSuperReferenceAbbrev(DebugContext context, byte[] buffer, int p) { + int pos = p; + + pos = writeAbbrevCode(DW_ABBREV_CODE_super_reference, buffer, pos); + pos = writeTag(DW_TAG_inheritance, buffer, pos); + pos = writeFlag(DW_CHILDREN_no, buffer, pos); + pos = writeAttrType(DW_AT_type, buffer, pos); + pos = writeAttrForm(DW_FORM_ref_addr, buffer, pos); + pos = writeAttrType(DW_AT_data_member_location, buffer, pos); + pos = writeAttrForm(DW_FORM_data1, buffer, pos); // = offset? in which segment though? + pos = writeAttrType(DW_AT_accessibility, buffer, pos); + pos = writeAttrForm(DW_FORM_data1, buffer, pos); // = offset? in which segment though? + /* + * Now terminate. + */ + pos = writeAttrType(DW_AT_null, buffer, pos); + pos = writeAttrForm(DW_FORM_null, buffer, pos); + return pos; + } + + private int writeParameterDeclarationAbbrevs(DebugContext context, byte[] buffer, int p) { + int pos = p; + pos = writeParameterDeclarationAbbrev(context, DW_ABBREV_CODE_method_parameter_declaration1, buffer, pos); + pos = writeParameterDeclarationAbbrev(context, DW_ABBREV_CODE_method_parameter_declaration2, buffer, pos); + pos = writeParameterDeclarationAbbrev(context, DW_ABBREV_CODE_method_parameter_declaration3, buffer, pos); + return pos; + } + + private int writeParameterDeclarationAbbrev(DebugContext context, int abbrevCode, byte[] buffer, int p) { + int pos = p; + pos = writeAbbrevCode(abbrevCode, buffer, pos); + pos = writeTag(DW_TAG_formal_parameter, buffer, pos); + pos = writeFlag(DW_CHILDREN_no, buffer, pos); + // pos = writeAttrType(DW_AT_name, buffer, pos); + // pos = writeAttrForm(DW_FORM_strp, buffer, pos); + if (abbrevCode == DW_ABBREV_CODE_method_parameter_declaration2) { + // don't always have a file name and line numbers are not yet available + pos = writeAttrType(DW_AT_decl_file, buffer, pos); + pos = writeAttrForm(DW_FORM_data2, buffer, pos); + // pos = writeAttrType(DW_AT_decl_line, buffer, pos); + // pos = writeAttrForm(DW_FORM_data2, buffer, pos); + } + pos = writeAttrType(DW_AT_type, buffer, pos); + pos = writeAttrForm(DW_FORM_ref_addr, buffer, pos); + if (abbrevCode == DW_ABBREV_CODE_method_parameter_declaration1) { + // only this parameter is artificial and it has no line + pos = writeAttrType(DW_AT_artificial, buffer, pos); + pos = writeAttrForm(DW_FORM_flag, buffer, pos); + } + // don't yet have locations for method parameters + // not even at the start of the method + // pos = writeAttrType(DW_AT_location, buffer, pos); + // pos = writeAttrForm(DW_FORM_data4, buffer, pos); /* * Now terminate. */ @@ -246,9 +1293,9 @@ private int writeMethodAbbrev(DebugContext context, byte[] buffer, int p) { } /** - * The debug_abbrev section content depends on debug_frame section content and offset. + * The debug_abbrev section depends on debug_aranges section. */ - private static final String TARGET_SECTION_NAME = DW_FRAME_SECTION_NAME; + private static final String TARGET_SECTION_NAME = DW_ARANGES_SECTION_NAME; @Override public String targetSectionName() { @@ -257,7 +1304,7 @@ public String targetSectionName() { private final LayoutDecision.Kind[] targetSectionKinds = { LayoutDecision.Kind.CONTENT, - LayoutDecision.Kind.OFFSET + LayoutDecision.Kind.SIZE }; @Override 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 449e3bb3d44f..0848607e168b 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 @@ -27,9 +27,12 @@ package com.oracle.objectfile.elf.dwarf; import java.nio.ByteOrder; +import java.util.HashMap; +import com.oracle.objectfile.debugentry.ClassEntry; import com.oracle.objectfile.debugentry.DebugInfoBase; +import com.oracle.objectfile.debugentry.TypeEntry; import com.oracle.objectfile.elf.ELFMachine; /** @@ -43,6 +46,7 @@ public class DwarfDebugInfo extends DebugInfoBase { * Names of the different ELF sections we create or reference in reverse dependency order. */ public static final String TEXT_SECTION_NAME = ".text"; + public static final String HEAP_BEGIN_NAME = "__svm_heap_begin"; public static final String DW_STR_SECTION_NAME = ".debug_str"; public static final String DW_LINE_SECTION_NAME = ".debug_line"; public static final String DW_FRAME_SECTION_NAME = ".debug_frame"; @@ -51,48 +55,112 @@ public class DwarfDebugInfo extends DebugInfoBase { public static final String DW_ARANGES_SECTION_NAME = ".debug_aranges"; /** - * Currently generated debug info relies on DWARF spec vesion 2. + * Currently generated debug info relies on DWARF spec version 4. */ public static final short DW_VERSION_2 = 2; + public static final short DW_VERSION_4 = 4; /* * Define all the abbrev section codes we need for our DIEs. */ @SuppressWarnings("unused") public static final int DW_ABBREV_CODE_null = 0; - public static final int DW_ABBREV_CODE_compile_unit_1 = 1; - public static final int DW_ABBREV_CODE_compile_unit_2 = 2; - public static final int DW_ABBREV_CODE_subprogram = 3; - + // level 0 DIEs + 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; + // 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_layout = 8; + public static final int DW_ABBREV_CODE_class_pointer = 9; + public static final int DW_ABBREV_CODE_class_typedef = 10; + public static final int DW_ABBREV_CODE_method_location = 11; + 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_array_typedef = 15; + public static final int DW_ABBREV_CODE_interface_layout = 16; + public static final int DW_ABBREV_CODE_interface_pointer = 17; + public static final int DW_ABBREV_CODE_interface_typedef = 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_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; + // 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; /* * Define all the Dwarf tags we need for our DIEs. */ + public static final int DW_TAG_array_type = 0x01; + public static final int DW_TAG_class_type = 0x02; + public static final int DW_TAG_formal_parameter = 0x05; + public static final int DW_TAG_member = 0x0d; + public static final int DW_TAG_pointer_type = 0x0f; public static final int DW_TAG_compile_unit = 0x11; + public static final int DW_TAG_structure_type = 0x13; + public static final int DW_TAG_typedef = 0x16; + public static final int DW_TAG_union_type = 0x17; + public static final int DW_TAG_inheritance = 0x1c; + public static final int DW_TAG_base_type = 0x24; 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; + /* * Define all the Dwarf attributes we need for our DIEs. */ public static final int DW_AT_null = 0x0; + public static final int DW_AT_location = 0x02; public static final int DW_AT_name = 0x3; - public static final int DW_AT_comp_dir = 0x1b; + public static final int DW_AT_byte_size = 0x0b; + public static final int DW_AT_bit_size = 0x0d; public static final int DW_AT_stmt_list = 0x10; public static final int DW_AT_low_pc = 0x11; public static final int DW_AT_hi_pc = 0x12; 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_accessibility = 0x32; + public static final int DW_AT_artificial = 0x34; + public static final int DW_AT_data_member_location = 0x38; + // public static final int DW_AT_decl_column = 0x39; + public static final int DW_AT_decl_file = 0x3a; + // public static final int DW_AT_decl_line = 0x3b; + public static final int DW_AT_declaration = 0x3c; + public static final int DW_AT_encoding = 0x3e; public static final int DW_AT_external = 0x3f; @SuppressWarnings("unused") public static final int DW_AT_return_addr = 0x2a; @SuppressWarnings("unused") public static final int DW_AT_frame_base = 0x40; + public static final int DW_AT_specification = 0x47; + public static final int DW_AT_type = 0x49; + public static final int Dw_AT_object_pointer = 0x64; + /* * Define all the Dwarf attribute forms we need for our DIEs. */ public static final int DW_FORM_null = 0x0; - @SuppressWarnings("unused") private static final int DW_FORM_string = 0x8; - public static final int DW_FORM_strp = 0xe; public static final int DW_FORM_addr = 0x1; - public static final int DW_FORM_data1 = 0x0b; + public static final int DW_FORM_data2 = 0x05; public static final int DW_FORM_data4 = 0x6; @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; + 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; + public static final int DW_FORM_strp = 0xe; + public static final int DW_FORM_expr_loc = 0x18; /* * Define specific attribute values for given attribute or form types. @@ -122,11 +190,14 @@ public class DwarfDebugInfo extends DebugInfoBase { @SuppressWarnings("unused") public static final byte DW_ACCESS_private = 3; /* - * Others that are not yet needed. + * DW_AT_encoding attribute values */ - @SuppressWarnings("unused") public static final int DW_AT_type = 0; // only present for non-void - // functions - @SuppressWarnings("unused") public static final int DW_AT_accessibility = 0; + public static final byte DW_ATE_address = 0x1; + public static final byte DW_ATE_boolean = 0x2; + public static final byte DW_ATE_float = 0x4; + public static final byte DW_ATE_signed = 0x5; + public static final byte DW_ATE_signed_char = 0x6; + public static final byte DW_ATE_unsigned = 0x7; /* * CIE and FDE entries. @@ -158,6 +229,21 @@ public class DwarfDebugInfo extends DebugInfoBase { @SuppressWarnings("unused") public static final byte DW_CFA_def_cfa_register = 0xd; public static final byte DW_CFA_def_cfa_offset = 0xe; + /* + * Values used to build DWARF expressions and locations + */ + public static final byte DW_OP_addr = 0x03; + public static final byte DW_OP_plus = 0x22; + public static final byte DW_OP_breg0 = 0x70; + public static final byte DW_OP_push_object_address = (byte) 0x97; + + // register constants for AArch64 + public static final byte rheapbase_aarch64 = (byte) 27; + public static final byte rthread_aarch64 = (byte) 28; + // register constants for x86 + public static final byte rheapbase_x86 = (byte) 14; + public static final byte rthread_x86 = (byte) 15; + private DwarfStrSectionImpl dwarfStrSection; private DwarfAbbrevSectionImpl dwarfAbbrevSection; private DwarfInfoSectionImpl dwarfInfoSection; @@ -165,6 +251,21 @@ public class DwarfDebugInfo extends DebugInfoBase { private DwarfLineSectionImpl dwarfLineSection; private DwarfFrameSectionImpl dwarfFameSection; public final ELFMachine elfMachine; + /** + * Register used to hold the heap base. + */ + private byte heapbaseRegister; + /** + * Register used to hold the current thread. + */ + private byte threadRegister; + + /** + * A collection of properties associated with each generated type record indexed by type name. + * n.b. this collection includes entries for the structure types used to define the object and + * array headers which do not have an associated TypeEntry. + */ + private HashMap propertiesIndex; public DwarfDebugInfo(ELFMachine elfMachine, ByteOrder byteOrder) { super(byteOrder); @@ -176,9 +277,14 @@ public DwarfDebugInfo(ELFMachine elfMachine, ByteOrder byteOrder) { dwarfLineSection = new DwarfLineSectionImpl(this); if (elfMachine == ELFMachine.AArch64) { dwarfFameSection = new DwarfFrameSectionImplAArch64(this); + this.heapbaseRegister = rheapbase_aarch64; + this.threadRegister = rthread_aarch64; } else { dwarfFameSection = new DwarfFrameSectionImplX86_64(this); + this.heapbaseRegister = rheapbase_x86; + this.threadRegister = rthread_x86; } + propertiesIndex = new HashMap<>(); } public DwarfStrSectionImpl getStrSectionImpl() { @@ -204,4 +310,333 @@ public DwarfARangesSectionImpl getARangesSectionImpl() { public DwarfLineSectionImpl getLineSectionImpl() { return dwarfLineSection; } + + public byte getHeapbaseRegister() { + return heapbaseRegister; + } + + public byte getThreadRegister() { + return threadRegister; + } + + /** + * A class used to associate properties with a specific type, the most important one being its + * index in the info section. + */ + static class DwarfTypeProperties { + /** + * index in debug_info section of type declaration for this class. + */ + private int typeInfoIndex; + /** + * The type entry with which these properties are associated. + */ + private final TypeEntry typeEntry; + + public int getTypeInfoIndex() { + return typeInfoIndex; + } + + public void setTypeInfoIndex(int typeInfoIndex) { + this.typeInfoIndex = typeInfoIndex; + } + + public TypeEntry getTypeEntry() { + return typeEntry; + } + + DwarfTypeProperties(TypeEntry typeEntry) { + this.typeEntry = typeEntry; + this.typeInfoIndex = -1; + } + + } + + /** + * A class used to associate extra properties with an instance class type. + */ + + static class DwarfClassProperties extends DwarfTypeProperties { + /** + * Index of debug_info section compilation unit for this class. + */ + private int cuIndex; + /** + * index of debug_info section compilation unit for deopt target methods. + */ + private int deoptCUIndex; + /** + * Index of the class entry's class_layout DIE in the debug_info section. + */ + private int layoutIndex; + /** + * Index of the class entry's pointer type for the class_layout DIE in the debug_info + * section. + */ + private int pointerIndex; + /** + * Index into debug_line section for associated compilation unit. + */ + private int lineIndex; + /** + * Size of line number info prologue region for associated compilation unit. + */ + private int linePrologueSize; + /** + * Total size of line number info region for associated compilation unit. + */ + private int lineSectionSize; + /** + * Map from field names to info section index for the field declaration. + */ + private HashMap fieldDeclarationIndex; + /** + * Map from method names to info section index for the field declaration. + */ + private HashMap methodDeclarationIndex; + + DwarfClassProperties(ClassEntry classEntry) { + super(classEntry); + this.cuIndex = -1; + this.deoptCUIndex = -1; + this.layoutIndex = -1; + this.pointerIndex = -1; + this.lineIndex = -1; + this.linePrologueSize = -1; + this.lineSectionSize = -1; + fieldDeclarationIndex = null; + methodDeclarationIndex = null; + } + } + + private DwarfTypeProperties addTypeProperties(TypeEntry typeEntry) { + assert typeEntry != null; + assert !typeEntry.isClass(); + String typeName = typeEntry.getTypeName(); + assert propertiesIndex.get(typeName) == null; + DwarfTypeProperties typeProperties = new DwarfTypeProperties(typeEntry); + this.propertiesIndex.put(typeName, typeProperties); + return typeProperties; + } + + private DwarfClassProperties addClassProperties(ClassEntry classEntry) { + String typeName = classEntry.getTypeName(); + assert propertiesIndex.get(typeName) == null; + DwarfClassProperties classProperties = new DwarfClassProperties(classEntry); + this.propertiesIndex.put(typeName, classProperties); + return classProperties; + } + + private DwarfTypeProperties lookupTypeProperties(TypeEntry typeEntry) { + if (typeEntry instanceof ClassEntry) { + return lookupClassProperties((ClassEntry) typeEntry); + } else { + String typeName = typeEntry.getTypeName(); + DwarfTypeProperties typeProperties = propertiesIndex.get(typeName); + if (typeProperties == null) { + typeProperties = addTypeProperties(typeEntry); + } + return typeProperties; + } + } + + private DwarfClassProperties lookupClassProperties(ClassEntry classEntry) { + String typeName = classEntry.getTypeName(); + DwarfTypeProperties typeProperties = propertiesIndex.get(typeName); + assert typeProperties == null || typeProperties instanceof DwarfClassProperties; + DwarfClassProperties classProperties = (DwarfClassProperties) typeProperties; + if (classProperties == null) { + classProperties = addClassProperties(classEntry); + } + return classProperties; + } + + private DwarfTypeProperties lookupTypeProperties(String typeName) { + DwarfTypeProperties typeProperties = propertiesIndex.get(typeName); + assert typeProperties != null; + assert typeProperties.getTypeEntry().getTypeName().equals(typeName); + return typeProperties; + } + + private DwarfClassProperties lookupClassProperties(String typeName) { + DwarfTypeProperties classProperties = propertiesIndex.get(typeName); + assert classProperties != null; + assert classProperties.getClass() == DwarfClassProperties.class; + assert classProperties.getTypeEntry().getTypeName().equals(typeName); + return (DwarfClassProperties) classProperties; + } + + void setTypeIndex(TypeEntry typeEntry, int idx) { + DwarfTypeProperties typeProperties = lookupTypeProperties(typeEntry); + assert typeProperties.getTypeInfoIndex() == -1 || typeProperties.getTypeInfoIndex() == idx; + typeProperties.setTypeInfoIndex(idx); + } + + int getTypeIndex(String typeName) { + DwarfTypeProperties typeProperties = lookupTypeProperties(typeName); + return getTypeIndex(typeProperties); + } + + int getTypeIndex(DwarfTypeProperties typeProperties) { + assert typeProperties.getTypeInfoIndex() >= 0; + return typeProperties.getTypeInfoIndex(); + } + + void setCUIndex(ClassEntry classEntry, int idx) { + DwarfClassProperties classProperties = lookupClassProperties(classEntry); + assert classProperties.getTypeEntry() == classEntry; + assert classProperties.cuIndex == -1 || classProperties.cuIndex == idx; + classProperties.cuIndex = idx; + } + + int getCUIndex(ClassEntry classEntry) { + DwarfClassProperties classProperties; + classProperties = lookupClassProperties(classEntry); + assert classProperties.getTypeEntry() == classEntry; + assert classProperties.cuIndex >= 0; + return classProperties.cuIndex; + } + + void setDeoptCUIndex(ClassEntry classEntry, int idx) { + DwarfClassProperties classProperties; + classProperties = lookupClassProperties(classEntry); + assert classProperties.getTypeEntry() == classEntry; + assert (classProperties.deoptCUIndex == -1 || classProperties.deoptCUIndex == idx); + classProperties.deoptCUIndex = idx; + } + + int getDeoptCUIndex(ClassEntry classEntry) { + DwarfClassProperties classProperties; + classProperties = lookupClassProperties(classEntry); + assert classProperties.getTypeEntry() == classEntry; + assert classProperties.deoptCUIndex >= 0; + return classProperties.deoptCUIndex; + } + + void setLayoutIndex(ClassEntry classEntry, int idx) { + DwarfClassProperties classProperties = lookupClassProperties(classEntry); + assert classProperties.getTypeEntry() == classEntry; + assert classProperties.layoutIndex == -1 || classProperties.layoutIndex == idx; + classProperties.layoutIndex = idx; + } + + int getLayoutIndex(ClassEntry classEntry) { + DwarfClassProperties classProperties; + classProperties = lookupClassProperties(classEntry); + assert classProperties.getTypeEntry() == classEntry; + assert classProperties.layoutIndex >= 0; + return classProperties.layoutIndex; + } + + void setPointerIndex(ClassEntry classEntry, int idx) { + DwarfClassProperties classProperties = lookupClassProperties(classEntry); + assert classProperties.getTypeEntry() == classEntry; + assert classProperties.pointerIndex == -1 || classProperties.pointerIndex == idx; + classProperties.pointerIndex = idx; + } + + int getPointerIndex(String typeName) { + DwarfClassProperties classProperties = lookupClassProperties(typeName); + assert classProperties.pointerIndex >= 0; + return classProperties.pointerIndex; + } + + void setLineIndex(ClassEntry classEntry, int idx) { + DwarfClassProperties classProperties; + classProperties = lookupClassProperties(classEntry); + assert classProperties.getTypeEntry() == classEntry; + assert (classProperties.lineIndex == -1 || classProperties.lineIndex == idx); + classProperties.lineIndex = idx; + } + + public int getLineIndex(ClassEntry classEntry) { + DwarfClassProperties classProperties; + classProperties = lookupClassProperties(classEntry); + assert classProperties.getTypeEntry() == classEntry; + // line index may be fetched without being set + assert classProperties.lineIndex >= -1; + return classProperties.lineIndex; + } + + public void setLinePrologueSize(ClassEntry classEntry, int prologueSize) { + DwarfClassProperties classProperties; + classProperties = lookupClassProperties(classEntry); + assert classProperties.getTypeEntry() == classEntry; + assert (classProperties.linePrologueSize == -1 || classProperties.linePrologueSize == prologueSize); + classProperties.linePrologueSize = prologueSize; + } + + public int getLinePrologueSize(ClassEntry classEntry) { + DwarfClassProperties classProperties; + classProperties = lookupClassProperties(classEntry); + assert classProperties.getTypeEntry() == classEntry; + assert classProperties.linePrologueSize >= 0; + return classProperties.linePrologueSize; + } + + public void setLineSectionSize(ClassEntry classEntry, int totalSize) { + DwarfClassProperties classProperties; + classProperties = lookupClassProperties(classEntry); + assert classProperties.getTypeEntry() == classEntry; + assert (classProperties.lineSectionSize == -1 || classProperties.lineSectionSize == totalSize); + classProperties.lineSectionSize = totalSize; + } + + public int getLineSectionSize(ClassEntry classEntry) { + DwarfClassProperties classProperties; + classProperties = lookupClassProperties(classEntry); + assert classProperties.getTypeEntry() == classEntry; + assert classProperties.lineSectionSize >= 0; + return classProperties.lineSectionSize; + } + + public void setFieldDeclarationIndex(ClassEntry classEntry, String fieldName, int pos) { + DwarfClassProperties classProperties; + classProperties = lookupClassProperties(classEntry); + assert classProperties.getTypeEntry() == classEntry; + HashMap fieldDeclarationIndex = classProperties.fieldDeclarationIndex; + if (fieldDeclarationIndex == null) { + classProperties.fieldDeclarationIndex = fieldDeclarationIndex = new HashMap<>(); + } + if (fieldDeclarationIndex.get(fieldName) != null) { + assert fieldDeclarationIndex.get(fieldName) == pos; + } else { + fieldDeclarationIndex.put(fieldName, pos); + } + } + + public int getFieldDeclarationIndex(ClassEntry classEntry, String fieldName) { + DwarfClassProperties classProperties; + classProperties = lookupClassProperties(classEntry); + assert classProperties.getTypeEntry() == classEntry; + HashMap fieldDeclarationIndex = classProperties.fieldDeclarationIndex; + assert fieldDeclarationIndex != null; + assert fieldDeclarationIndex.get(fieldName) != null; + return fieldDeclarationIndex.get(fieldName); + } + + public void setMethodDeclarationIndex(ClassEntry classEntry, String methodName, int pos) { + DwarfClassProperties classProperties; + classProperties = lookupClassProperties(classEntry); + assert classProperties.getTypeEntry() == classEntry; + HashMap methodDeclarationIndex = classProperties.methodDeclarationIndex; + if (methodDeclarationIndex == null) { + classProperties.methodDeclarationIndex = methodDeclarationIndex = new HashMap<>(); + } + if (methodDeclarationIndex.get(methodName) != null) { + assert methodDeclarationIndex.get(methodName) == pos; + } else { + methodDeclarationIndex.put(methodName, pos); + } + } + + public int getMethodDeclarationIndex(ClassEntry classEntry, String methodName) { + DwarfClassProperties classProperties; + classProperties = lookupClassProperties(classEntry); + assert classProperties.getTypeEntry() == classEntry; + HashMap methodDeclarationIndex = classProperties.methodDeclarationIndex; + assert methodDeclarationIndex != null; + assert methodDeclarationIndex.get(methodName) != null; + return methodDeclarationIndex.get(methodName); + } } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImpl.java index 800b8f4d5e55..b131a054a8eb 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImpl.java @@ -68,6 +68,8 @@ public String getSectionName() { @Override public void createContent() { + assert !contentByteArrayCreated(); + int pos = 0; /* @@ -82,6 +84,8 @@ public void createContent() { @Override public void writeContent(DebugContext context) { + assert contentByteArrayCreated(); + byte[] buffer = getContent(); int size = buffer.length; int pos = 0; @@ -386,7 +390,7 @@ protected int writeRegister(int savedReg, int savedToReg, byte[] buffer, int p) protected abstract int writeInitialInstructions(byte[] buffer, int pos); /** - * The debug_frame section content depends on debug_line section content and offset. + * The debug_frame section depends on debug_line section. */ private static final String TARGET_SECTION_NAME = DW_LINE_SECTION_NAME; @@ -397,7 +401,7 @@ public String targetSectionName() { private final LayoutDecision.Kind[] targetSectionKinds = { LayoutDecision.Kind.CONTENT, - LayoutDecision.Kind.OFFSET + LayoutDecision.Kind.SIZE }; @Override 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 c6e7b37a26fc..aa88674897a1 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 @@ -26,27 +26,93 @@ package com.oracle.objectfile.elf.dwarf; +import com.oracle.objectfile.BuildDependency; import com.oracle.objectfile.LayoutDecision; +import com.oracle.objectfile.LayoutDecisionMap; +import com.oracle.objectfile.ObjectFile; +import com.oracle.objectfile.debugentry.ArrayTypeEntry; import com.oracle.objectfile.debugentry.ClassEntry; +import com.oracle.objectfile.debugentry.FieldEntry; +import com.oracle.objectfile.debugentry.FileEntry; +import com.oracle.objectfile.debugentry.HeaderTypeEntry; +import com.oracle.objectfile.debugentry.InterfaceClassEntry; import com.oracle.objectfile.debugentry.PrimaryEntry; +import com.oracle.objectfile.debugentry.PrimitiveTypeEntry; import com.oracle.objectfile.debugentry.Range; +import com.oracle.objectfile.debugentry.StructureTypeEntry; +import com.oracle.objectfile.debugentry.TypeEntry; +import com.oracle.objectfile.elf.ELFObjectFile; import org.graalvm.compiler.debug.DebugContext; +import java.lang.reflect.Modifier; import java.util.LinkedList; +import java.util.Map; +import java.util.Set; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_compile_unit_1; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_compile_unit_2; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_subprogram; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_SECTION_NAME; +import static com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugPrimitiveTypeInfo.FLAG_INTEGRAL; +import static com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugPrimitiveTypeInfo.FLAG_NUMERIC; +import static com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugPrimitiveTypeInfo.FLAG_SIGNED; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_array_data_type; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_array_layout; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_array_pointer; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_array_typedef; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_array_unit; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_builtin_unit; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_class_layout; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_class_pointer; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_class_typedef; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_class_unit1; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_class_unit2; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_field_declaration1; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_field_declaration2; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_field_declaration3; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_field_declaration4; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_header_field; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_interface_implementor; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_interface_layout; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_interface_pointer; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_interface_typedef; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_method_declaration1; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_method_declaration2; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_method_location; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_method_parameter_declaration1; +// import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_method_parameter_declaration2; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_method_parameter_declaration3; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_object_header; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_primitive_type; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_static_field_location; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_super_reference; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_void_type; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ACCESS_private; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ACCESS_protected; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ACCESS_public; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ATE_boolean; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ATE_float; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ATE_signed; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ATE_signed_char; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ATE_unsigned; import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_FLAG_true; import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_INFO_SECTION_NAME; import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_LANG_Java; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_VERSION_2; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_OP_addr; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_OP_breg0; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_VERSION_4; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.TEXT_SECTION_NAME; /** * Section generator for debug_info section. */ public class DwarfInfoSectionImpl extends DwarfSectionImpl { + /** + * The name of a special DWARF struct type used to model an object header. + */ + public static final String OBJECT_HEADER_STRUCT_NAME = "_objhdr"; + + /** + * The name of a special DWARF struct type used to model an array header. + */ + public static final String ARRAY_HEADER_STRUCT_NAME = "_arrhdr"; + /** * an info header section always contains a fixed number of bytes. */ @@ -61,133 +127,1036 @@ public String getSectionName() { return DW_INFO_SECTION_NAME; } + @Override + public Set getDependencies(Map decisions) { + Set deps = super.getDependencies(decisions); + LayoutDecision ourContent = decisions.get(getElement()).getDecision(LayoutDecision.Kind.CONTENT); + // order all content decisions after all size decisions by + // making info section content depend on abbrev section size + String abbrevSectionName = dwarfSections.getAbbrevSectionImpl().getSectionName(); + ELFObjectFile.ELFSection abbrevSection = (ELFObjectFile.ELFSection) getElement().getOwner().elementForName(abbrevSectionName); + LayoutDecision sizeDecision = decisions.get(abbrevSection).getDecision(LayoutDecision.Kind.SIZE); + deps.add(BuildDependency.createOrGet(ourContent, sizeDecision)); + return deps; + } + @Override public void createContent() { - /* - * We need a single level 0 DIE for each compilation unit (CU). Each CU's Level 0 DIE is - * preceded by a fixed header and terminated by a null DIE: - * - *
            - * - *
          • uint32 length ......... excluding this length field - * - *
          • uint16 dwarf_version .. always 2 ?? - * - *
          • uint32 abbrev offset .. always 0 ?? - * - *
          • uint8 address_size .... always 8 - * - *
          • DIE* .................. sequence of top-level and nested child entries - * - *
          • null_DIE .............. == 0 - * - *
          - * - * A DIE is a recursively defined structure. it starts with a code for the associated abbrev - * entry followed by a series of attribute values, as determined by the entry, terminated by - * a null value and followed by zero or more child DIEs (zero iff has_children == - * no_children). - * - *
            - * - *
          • LEB128 abbrev_code != 0 .. non-zero value indexes tag + attr layout of - * DIE - * - *
          • attribute_value* ......... value sequence as determined by abbrev entry - * - *
          • DIE* ..................... sequence of child DIEs (if appropriate) - *
          • - * - *
          • null_value ............... == 0 - * - *
          - * - * Note that a null_DIE looks like: - * - *
            - * - *
          • LEB128 abbrev_code ....... == 0 - * - *
          - * - * i.e. it also looks like a null_value. - */ + assert !contentByteArrayCreated(); byte[] buffer = null; - int pos = 0; + int len = generateContent(null, buffer); - /* CUs for normal methods */ - for (ClassEntry classEntry : getPrimaryClasses()) { - int lengthPos = pos; - pos = writeCUHeader(buffer, pos); - assert pos == lengthPos + DW_DIE_HEADER_SIZE; - pos = writeCU(null, classEntry, false, buffer, pos); - /* - * No need to backpatch length at lengthPos. - */ - } - /* CUs for deopt targets */ - for (ClassEntry classEntry : getPrimaryClasses()) { - if (classEntry.includesDeoptTarget()) { - int lengthPos = pos; - pos = writeCUHeader(buffer, pos); - assert pos == lengthPos + DW_DIE_HEADER_SIZE; - pos = writeCU(null, classEntry, true, buffer, pos); - /* - * No need to backpatch length at lengthPos. - */ - } - } - buffer = new byte[pos]; + buffer = new byte[len]; super.setContent(buffer); } @Override public void writeContent(DebugContext context) { + assert contentByteArrayCreated(); + byte[] buffer = getContent(); int size = buffer.length; int pos = 0; enableLog(context, pos); - log(context, " [0x%08x] DEBUG_INFO", pos); log(context, " [0x%08x] size = 0x%08x", pos, size); - /* write CUs for normal methods */ - for (ClassEntry classEntry : getPrimaryClasses()) { - /* - * Save the offset of this file's CU so it can be used when writing the aranges section. - */ - classEntry.setCUIndex(pos); - int lengthPos = pos; - pos = writeCUHeader(buffer, pos); - log(context, " [0x%08x] Compilation Unit", pos, size); - assert pos == lengthPos + DW_DIE_HEADER_SIZE; - pos = writeCU(context, classEntry, false, buffer, pos); - /* - * Backpatch length at lengthPos (excluding length field). - */ - patchLength(lengthPos, buffer, pos); + + pos = generateContent(context, buffer); + assert pos == size; + } + + byte computeEncoding(int flags, int bitCount) { + assert bitCount > 0; + if ((flags & FLAG_NUMERIC) != 0) { + if (((flags & FLAG_INTEGRAL) != 0)) { + if ((flags & FLAG_SIGNED) != 0) { + switch (bitCount) { + case 8: + return DW_ATE_signed_char; + default: + assert bitCount == 16 || bitCount == 32 || bitCount == 64; + return DW_ATE_signed; + } + } else { + assert bitCount == 16; + return DW_ATE_unsigned; // should be UTF??? + } + } else { + assert bitCount == 32 || bitCount == 64; + return DW_ATE_float; + } + } else { + assert bitCount == 1; + return DW_ATE_boolean; } - /* write CUs for deopt targets */ + } + + public int generateContent(DebugContext context, byte[] buffer) { + int pos = 0; + pos = writeBuiltInUnit(context, buffer, pos); + + // write entries for all the types known to the generator + + // write class units for non-primary classes i.e. ones which + // don't have associated methods + + pos = writeNonPrimaryClasses(context, buffer, pos); + + // write class units for primary classes in increasing order + // of method address + + pos = writePrimaryClasses(context, buffer, pos); + + // write class units for array types + + pos = writeArrayTypes(context, buffer, pos); + + /* + * write extra special CUs for deopt targets. these are written out of line from the class + * because they are compiled later and hence inhabit a range that extends beyond the normal + * method address range. + */ for (ClassEntry classEntry : getPrimaryClasses()) { if (classEntry.includesDeoptTarget()) { /* * Save the offset of this file's CU so it can be used when writing the aranges * section. */ - classEntry.setDeoptCUIndex(pos); + setDeoptCUIndex(classEntry, pos); int lengthPos = pos; pos = writeCUHeader(buffer, pos); - log(context, " [0x%08x] Compilation Unit (deopt targets)", pos, size); + log(context, " [0x%08x] Compilation Unit (deopt targets)", pos); assert pos == lengthPos + DW_DIE_HEADER_SIZE; - pos = writeCU(context, classEntry, true, buffer, pos); + pos = writeDeoptMethodsCU(context, classEntry, buffer, pos); /* * Backpatch length at lengthPos (excluding length field). */ patchLength(lengthPos, buffer, pos); } } - assert pos == size; + + return pos; + } + + public int writeBuiltInUnit(DebugContext context, byte[] buffer, int pos) { + int lengthPos = pos; + log(context, " [0x%08x] <0> builtin unit", pos); + pos = writeCUHeader(buffer, pos); + assert pos == lengthPos + DW_DIE_HEADER_SIZE; + int abbrevCode = DW_ABBREV_CODE_builtin_unit; + 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"); + pos = writeAttrData1(DW_LANG_Java, buffer, pos); + + // write child entries for basic Java types + + pos = getTypes().filter(TypeEntry::isPrimitive).reduce(pos, + (p, typeEntry) -> { + PrimitiveTypeEntry primitiveTypeEntry = (PrimitiveTypeEntry) typeEntry; + if (primitiveTypeEntry.getBitCount() > 0) { + return writePrimitiveType(context, primitiveTypeEntry, buffer, p); + } else { + return writeVoidType(context, primitiveTypeEntry, buffer, p); + } + }, + (oldpos, newpos) -> newpos); + + // write child entries for object header and array header structs + + pos = getTypes().filter(TypeEntry::isHeader).reduce(pos, + (p, typeEntry) -> { + HeaderTypeEntry headerTypeEntry = (HeaderTypeEntry) typeEntry; + return writeHeaderType(context, headerTypeEntry, buffer, p); + }, + (oldpos, newpos) -> newpos); + + // terminate with null entry + + pos = writeAttrNull(buffer, pos); + + // fix up the CU length + + patchLength(lengthPos, buffer, pos); + + return pos; + } + + public int writePrimitiveType(DebugContext context, PrimitiveTypeEntry primitiveTypeEntry, byte[] buffer, int p) { + assert primitiveTypeEntry.getBitCount() > 0; + int pos = p; + log(context, " [0x%08x] primitive type %s", pos, primitiveTypeEntry.getTypeName()); + // record the location of this type entry + setTypeIndex(primitiveTypeEntry, pos); + int abbrevCode = DW_ABBREV_CODE_primitive_type; + log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); + pos = writeAbbrevCode(abbrevCode, buffer, pos); + byte byteSize = (byte) primitiveTypeEntry.getSize(); + log(context, " [0x%08x] byte_size %d", pos, byteSize); + pos = writeAttrData1(byteSize, buffer, pos); + byte bitCount = (byte) primitiveTypeEntry.getBitCount(); + log(context, " [0x%08x] bitCount %d", pos, bitCount); + pos = writeAttrData1(bitCount, buffer, pos); + byte encoding = computeEncoding(primitiveTypeEntry.getFlags(), bitCount); + log(context, " [0x%08x] encoding 0x%x", pos, encoding); + pos = writeAttrData1(encoding, buffer, pos); + String name = primitiveTypeEntry.getTypeName(); + log(context, " [0x%08x] name 0x%x (%s)", pos, debugStringIndex(name), name); + return writeAttrStrp(name, buffer, pos); + } + + public int writeVoidType(DebugContext context, PrimitiveTypeEntry primitiveTypeEntry, byte[] buffer, int p) { + assert primitiveTypeEntry.getBitCount() == 0; + int pos = p; + log(context, " [0x%08x] primitive type void", pos); + // record the location of this type entry + setTypeIndex(primitiveTypeEntry, pos); + int abbrevCode = DW_ABBREV_CODE_void_type; + log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); + pos = writeAbbrevCode(abbrevCode, buffer, pos); + String name = primitiveTypeEntry.getTypeName(); + log(context, " [0x%08x] name 0x%x (%s)", pos, debugStringIndex(name), name); + return writeAttrStrp(name, buffer, pos); + } + + public int writeHeaderType(DebugContext context, HeaderTypeEntry headerTypeEntry, byte[] buffer, int p) { + int pos = p; + String name = headerTypeEntry.getTypeName(); + byte size = (byte) headerTypeEntry.getSize(); + log(context, " [0x%08x] header type %s", pos, name); + // record the location of this type entry + setTypeIndex(headerTypeEntry, pos); + int abbrevCode = DW_ABBREV_CODE_object_header; + log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); + pos = writeAbbrevCode(abbrevCode, buffer, pos); + log(context, " [0x%08x] name 0x%x (%s)", pos, debugStringIndex(name), name); + pos = writeAttrStrp(name, buffer, pos); + log(context, " [0x%08x] byte_size 0x%x", pos, size); + pos = writeAttrData1(size, buffer, pos); + + pos = writeHeaderFields(context, headerTypeEntry, buffer, pos); + /* + * Write a terminating null attribute. + */ + return writeAttrNull(buffer, pos); + } + + private int writeHeaderFields(DebugContext context, HeaderTypeEntry headerTypeEntry, byte[] buffer, int p) { + return headerTypeEntry.fields().reduce(p, + (pos, fieldEntry) -> writeHeaderField(context, fieldEntry, buffer, pos), + (oldPos, newPos) -> newPos); + } + + private int writeHeaderField(DebugContext context, FieldEntry fieldEntry, byte[] buffer, int p) { + int pos = p; + String fieldName = fieldEntry.fieldName(); + TypeEntry valueType = fieldEntry.getValueType(); + String valueTypeName = valueType.getTypeName(); + int valueTypeIdx = getTypeIndex(valueTypeName); + log(context, " [0x%08x] header field", pos); + int abbrevCode = DW_ABBREV_CODE_header_field; + log(context, " [0x%08x] <2> Abbrev Number %d", pos, abbrevCode); + pos = writeAbbrevCode(abbrevCode, buffer, pos); + log(context, " [0x%08x] name 0x%x (%s)", pos, debugStringIndex(fieldName), fieldName); + pos = writeAttrStrp(fieldName, buffer, pos); + log(context, " [0x%08x] type 0x%x (%s)", pos, valueTypeIdx, valueTypeName); + pos = writeAttrRefAddr(valueTypeIdx, buffer, pos); + byte offset = (byte) fieldEntry.getOffset(); + int size = fieldEntry.getSize(); + log(context, " [0x%08x] offset 0x%x (size 0x%x)", pos, offset, size); + pos = writeAttrData1(offset, buffer, pos); + int modifiers = fieldEntry.getModifiers(); + log(context, " [0x%08x] modifiers %s", pos, fieldEntry.getModifiersString()); + return writeAttrAccessibility(modifiers, buffer, pos); + } + + private int writeNonPrimaryClasses(DebugContext context, byte[] buffer, int pos) { + log(context, " [0x%08x] non primary classes", pos); + return getTypes().filter(TypeEntry::isClass).reduce(pos, + (p, typeEntry) -> { + ClassEntry classEntry = (ClassEntry) typeEntry; + return (classEntry.isPrimary() ? p : writeNonPrimaryClassUnit(context, classEntry, buffer, p)); + }, + (oldpos, newpos) -> newpos); + + } + + private int writeNonPrimaryClassUnit(DebugContext context, ClassEntry classEntry, byte[] buffer, int p) { + int pos = p; + setCUIndex(classEntry, pos); + int lengthPos = pos; + log(context, " [0x%08x] non primary class unit %s", pos, classEntry.getTypeName()); + 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 = DW_ABBREV_CODE_class_unit2; + 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"); + pos = writeAttrData1(DW_LANG_Java, buffer, pos); + log(context, " [0x%08x] name 0x%x (%s)", pos, debugStringIndex(classEntry.getFileName()), classEntry.getFileName()); + pos = writeAttrStrp(classEntry.getFileName(), buffer, pos); + String compilationDirectory = classEntry.getCachePath(); + log(context, " [0x%08x] comp_dir 0x%x (%s)", pos, debugStringIndex(compilationDirectory), compilationDirectory); + pos = writeAttrStrp(compilationDirectory, buffer, pos); + // 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); + // n.b. 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 + + if (classEntry.isInterface()) { + InterfaceClassEntry interfaceClassEntry = (InterfaceClassEntry) classEntry; + pos = writeInterfaceLayout(context, interfaceClassEntry, buffer, pos); + pos = writeInterfaceType(context, interfaceClassEntry, buffer, pos); + } else { + pos = writeClassLayout(context, classEntry, buffer, pos); + pos = writeClassType(context, classEntry, buffer, pos); + } + + // for a non-primary there are no method definitions to write + + // write all static field definitions + + pos = writeStaticFieldLocations(context, classEntry, buffer, pos); + + // terminate children with null entry + + pos = writeAttrNull(buffer, pos); + + // fix up the CU length + + patchLength(lengthPos, buffer, pos); + + return pos; + } + + private int writePrimaryClasses(DebugContext context, byte[] buffer, int pos) { + log(context, " [0x%08x] primary classes", pos); + return getTypes().filter(TypeEntry::isClass).reduce(pos, + (p, typeEntry) -> { + ClassEntry classEntry = (ClassEntry) typeEntry; + return (classEntry.isPrimary() ? writePrimaryClassUnit(context, classEntry, buffer, p) : p); + }, + (oldpos, newpos) -> newpos); + } + + private int writePrimaryClassUnit(DebugContext context, ClassEntry classEntry, byte[] buffer, int p) { + 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 + int abbrevCode = (fileName.length() > 0 ? DW_ABBREV_CODE_class_unit1 : DW_ABBREV_CODE_class_unit2); + setCUIndex(classEntry, pos); + int lengthPos = pos; + log(context, " [0x%08x] primary class unit %s", pos, classEntry.getTypeName()); + pos = writeCUHeader(buffer, pos); + assert pos == lengthPos + DW_DIE_HEADER_SIZE; + 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"); + pos = writeAttrData1(DW_LANG_Java, buffer, pos); + log(context, " [0x%08x] name 0x%x (%s)", pos, debugStringIndex(fileName), fileName); + pos = writeAttrStrp(fileName, buffer, pos); + String compilationDirectory = classEntry.getCachePath(); + log(context, " [0x%08x] comp_dir 0x%x (%s)", pos, debugStringIndex(compilationDirectory), compilationDirectory); + pos = writeAttrStrp(compilationDirectory, buffer, pos); + LinkedList 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 + if (abbrevCode == DW_ABBREV_CODE_class_unit1) { + log(context, " [0x%08x] stmt_list 0x%08x", pos, lineIndex); + pos = writeAttrData4(lineIndex, buffer, pos); + } + + // now write the child DIEs starting with the layout and pointer type + + if (classEntry.isInterface()) { + InterfaceClassEntry interfaceClassEntry = (InterfaceClassEntry) classEntry; + pos = writeInterfaceLayout(context, interfaceClassEntry, buffer, pos); + pos = writeInterfaceType(context, interfaceClassEntry, buffer, pos); + } else { + pos = writeClassLayout(context, classEntry, buffer, pos); + pos = writeClassType(context, classEntry, buffer, pos); + } + + // write all method locations + + pos = writeMethodLocations(context, classEntry, buffer, pos); + + // write all static field definitions + + pos = writeStaticFieldLocations(context, classEntry, buffer, pos); + + // terminate children with null entry + + pos = writeAttrNull(buffer, pos); + + // fix up the CU length + + patchLength(lengthPos, buffer, pos); + + return pos; + } + + private int writeClassLayout(DebugContext context, ClassEntry classEntry, byte[] buffer, int p) { + int pos = p; + setLayoutIndex(classEntry, pos); + log(context, " [0x%08x] class layout", pos); + int abbrevCode = DW_ABBREV_CODE_class_layout; + log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); + pos = writeAbbrevCode(abbrevCode, buffer, pos); + String name = uniqueDebugString("_" + classEntry.getTypeName()); + log(context, " [0x%08x] name 0x%x (%s)", pos, debugStringIndex(name), name); + pos = writeAttrStrp(name, buffer, pos); + int size = classEntry.getSize(); + log(context, " [0x%08x] byte_size 0x%x", pos, size); + pos = writeAttrData2((short) size, buffer, pos); + int fileIdx = classEntry.localFilesIdx(classEntry.getFileEntry()); + log(context, " [0x%08x] file 0x%x (%s)", pos, fileIdx, classEntry.getFileName()); + pos = writeAttrData2((short) fileIdx, buffer, pos); + ClassEntry superClassEntry = classEntry.getSuperClass(); + // n.b. the containing_type attribute is not strict DWARF but gdb expects it + // we also add an inheritance member with the same info + int superTypeOffset; + String superName; + if (superClassEntry != null) { + // inherit layout from super class + superName = superClassEntry.getTypeName(); + superTypeOffset = getLayoutIndex(superClassEntry); + } else { + // inherit layout from object header + superName = OBJECT_HEADER_STRUCT_NAME; + superTypeOffset = getTypeIndex(superName); + } + log(context, " [0x%08x] containing_type 0x%x (%s)", pos, superTypeOffset, superName); + pos = writeAttrRefAddr(superTypeOffset, buffer, pos); + pos = writeSuperReference(context, superTypeOffset, superName, buffer, pos); + pos = writeFields(context, classEntry, buffer, pos); + pos = writeMethodDeclarations(context, classEntry, buffer, pos); + /* + * Write a terminating null attribute. + */ + return writeAttrNull(buffer, pos); + } + + private int writeSuperReference(DebugContext context, int superTypeOffset, String superName, byte[] buffer, int pos) { + log(context, " [0x%08x] super reference", pos); + int abbrevCode = DW_ABBREV_CODE_super_reference; + log(context, " [0x%08x] <2> Abbrev Number %d", pos, abbrevCode); + pos = writeAbbrevCode(abbrevCode, buffer, pos); + log(context, " [0x%08x] type 0x%x (%s)", pos, superTypeOffset, superName); + pos = writeAttrRefAddr(superTypeOffset, buffer, pos); + // parent layout is embedded at start of object + log(context, " [0x%08x] data_member_location (super) 0x%x", pos, 0); + pos = writeAttrData1((byte) 0, buffer, pos); + log(context, " [0x%08x] modifiers public", pos); + int modifiers = Modifier.PUBLIC; + pos = writeAttrAccessibility(modifiers, buffer, pos); + return pos; + } + + private int writeFields(DebugContext context, ClassEntry classEntry, byte[] buffer, int p) { + return classEntry.fields().filter(DwarfInfoSectionImpl::isManifestedField).reduce(p, + (pos, fieldEntry) -> writeField(context, classEntry, fieldEntry, buffer, pos), + (oldPos, newPos) -> newPos); + } + + private static boolean isManifestedField(FieldEntry fieldEntry) { + return fieldEntry.getOffset() >= 0; + } + + private int writeField(DebugContext context, ClassEntry classEntry, FieldEntry fieldEntry, byte[] buffer, int p) { + int pos = p; + int modifiers = fieldEntry.getModifiers(); + boolean hasFile = classEntry.getFileName().length() > 0; + log(context, " [0x%08x] field definition", pos); + int abbrevCode; + boolean isStatic = Modifier.isStatic(modifiers); + if (!isStatic) { + if (!hasFile) { + abbrevCode = DW_ABBREV_CODE_field_declaration1; + } else { + abbrevCode = DW_ABBREV_CODE_field_declaration2; + } + } else { + if (!hasFile) { + abbrevCode = DW_ABBREV_CODE_field_declaration3; + } else { + abbrevCode = DW_ABBREV_CODE_field_declaration4; + } + // record the position of the declaration to use when we write the definition + setFieldDeclarationIndex(classEntry, fieldEntry.fieldName(), pos); + } + log(context, " [0x%08x] <2> Abbrev Number %d", pos, abbrevCode); + pos = writeAbbrevCode(abbrevCode, buffer, pos); + + String name = fieldEntry.fieldName(); + log(context, " [0x%08x] name 0x%x (%s)", pos, debugStringIndex(name), name); + pos = writeAttrStrp(name, buffer, pos); + // we may not have a file and line for a field + if (hasFile) { + int fileIdx = classEntry.localFilesIdx(classEntry.getFileEntry()); + log(context, " [0x%08x] filename 0x%x (%s)", pos, fileIdx, classEntry.getFileName()); + pos = writeAttrData2((short) fileIdx, buffer, pos); + // at present we definitely don't have line numbers + } + String valueTypeName = fieldEntry.getValueType().getTypeName(); + // static fields never store compressed values instance fields may do + int typeIdx = getTypeIndex(valueTypeName); + log(context, " [0x%08x] type 0x%x (%s)", pos, typeIdx, valueTypeName); + pos = writeAttrRefAddr(typeIdx, buffer, pos); + if (!isStatic) { + int memberOffset = fieldEntry.getOffset(); + log(context, " [0x%08x] member offset 0x%x", pos, memberOffset); + pos = writeAttrData2((short) memberOffset, buffer, pos); + } + log(context, " [0x%08x] accessibility %s", pos, fieldEntry.getModifiersString()); + pos = writeAttrAccessibility(fieldEntry.getModifiers(), buffer, pos); + // static fields are only declared here and are external + if (isStatic) { + log(context, " [0x%08x] external(true)", pos); + pos = writeFlag((byte) 1, buffer, pos); + log(context, " [0x%08x] definition(true)", pos); + pos = writeFlag((byte) 1, buffer, pos); + } + return pos; + } + + private int writeMethodDeclarations(DebugContext context, ClassEntry classEntry, byte[] buffer, int p) { + int pos = p; + LinkedList classPrimaryEntries = classEntry.getPrimaryEntries(); + for (PrimaryEntry primaryEntry : classPrimaryEntries) { + Range range = primaryEntry.getPrimary(); + // declare all methods including deopt targets even though they are written in separate + // CUs. + pos = writeMethodDeclaration(context, classEntry, range, buffer, pos); + } + + return pos; + } + + private int writeMethodDeclaration(DebugContext context, ClassEntry classEntry, Range range, byte[] buffer, int p) { + int pos = p; + String methodKey = range.getFullMethodNameWithParamsAndReturnType(); + setMethodDeclarationIndex(classEntry, methodKey, pos); + int modifiers = range.getModifiers(); + boolean isStatic = Modifier.isStatic(modifiers); + log(context, " [0x%08x] method declaration %s", pos, methodKey); + int abbrevCode = (isStatic ? DW_ABBREV_CODE_method_declaration2 : DW_ABBREV_CODE_method_declaration1); + log(context, " [0x%08x] <2> Abbrev Number %d", pos, abbrevCode); + pos = writeAbbrevCode(abbrevCode, buffer, pos); + log(context, " [0x%08x] external true", pos); + pos = writeFlag((byte) 1, buffer, pos); + String name = uniqueDebugString(range.getMethodName()); + log(context, " [0x%08x] name 0x%x (%s)", pos, debugStringIndex(name), name); + pos = writeAttrStrp(name, buffer, pos); + int fileIdx = classEntry.localFilesIdx(range.getFileEntry()); + log(context, " [0x%08x] file 0x%x (%s)", pos, fileIdx, range.getFileEntry().getFullName()); + pos = writeAttrData2((short) fileIdx, buffer, pos); + String returnTypeName = range.getMethodReturnTypeName(); + 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, range.isDeoptTarget() ? "true" : "false"); + pos = writeFlag((range.isDeoptTarget() ? (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); + pos = writeFlag((byte) 1, buffer, pos); + int typeIdx = getLayoutIndex(classEntry); + log(context, " [0x%08x] containing_type 0x%x (%s)", pos, typeIdx, classEntry.getTypeName()); + pos = writeAttrRefAddr(typeIdx, buffer, pos); + if (abbrevCode == DW_ABBREV_CODE_method_declaration1) { + // record the current position so we can back patch the object pointer + int objectPointerIndex = pos; + // write a dummy ref address to move pos on to where the first parameter gets written + pos = writeAttrRefAddr(0, buffer, pos); + // now backpatch object pointer slot with current pos, identifying the first parameter + log(context, " [0x%08x] object_pointer 0x%x", objectPointerIndex, pos); + writeAttrRefAddr(pos, buffer, objectPointerIndex); + } + // write method parameter declarations + pos = writeMethodParameterDeclarations(context, classEntry, range, true, buffer, pos); + /* + * Write a terminating null attribute. + */ + return writeAttrNull(buffer, pos); + } + + private int writeMethodParameterDeclarations(DebugContext context, ClassEntry classEntry, Range range, boolean isSpecification, byte[] buffer, int p) { + int pos = p; + if (!Modifier.isStatic(range.getModifiers())) { + pos = writeMethodParameterDeclaration(context, "this", classEntry.getTypeName(), true, isSpecification, buffer, pos); + } + String paramsString = range.getParamSignature(); + if (!paramsString.isEmpty()) { + String[] paramTypes = paramsString.split(","); + for (int i = 0; i < paramTypes.length; i++) { + String paramName = uniqueDebugString(""); + String paramTypeName = paramTypes[i].trim(); + FileEntry fileEntry = range.getFileEntry(); + if (fileEntry != null) { + pos = writeMethodParameterDeclaration(context, paramName, paramTypeName, false, isSpecification, buffer, pos); + } else { + pos = writeMethodParameterDeclaration(context, paramTypeName, paramTypeName, false, isSpecification, buffer, pos); + } + } + } + return pos; + } + + private int writeMethodParameterDeclaration(DebugContext context, String paramName, String paramTypeName, boolean artificial, boolean isSpecification, byte[] buffer, int p) { + int pos = p; + log(context, " [0x%08x] method parameter declaration", pos); + int abbrevCode; + int level = (isSpecification ? 3 : 2); + if (artificial) { + abbrevCode = DW_ABBREV_CODE_method_parameter_declaration1; + } else { + abbrevCode = DW_ABBREV_CODE_method_parameter_declaration3; + } + log(context, " [0x%08x] <%d> Abbrev Number %d", pos, level, abbrevCode); + pos = writeAbbrevCode(abbrevCode, buffer, pos); + // an artificial 'this' parameter has to be typed using the raw pointer type. + // other parameters can be typed using the typedef that retains the Java type name + int typeIdx = (artificial ? getPointerIndex(paramTypeName) : getTypeIndex(paramTypeName)); + log(context, " [0x%08x] type 0x%x (%s)", pos, typeIdx, paramTypeName); + pos = writeAttrRefAddr(typeIdx, buffer, pos); + if (abbrevCode == DW_ABBREV_CODE_method_parameter_declaration1) { + log(context, " [0x%08x] artificial true", pos); + pos = writeFlag((byte) 1, buffer, pos); + } + return pos; + } + + private int writeInterfaceLayout(DebugContext context, InterfaceClassEntry interfaceClassEntry, byte[] buffer, int p) { + int pos = p; + int layoutOffset = pos; + setLayoutIndex(interfaceClassEntry, layoutOffset); + log(context, " [0x%08x] interface layout", pos); + int abbrevCode = DW_ABBREV_CODE_interface_layout; + log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); + pos = writeAbbrevCode(abbrevCode, buffer, pos); + String name = uniqueDebugString("_" + interfaceClassEntry.getTypeName()); + log(context, " [0x%08x] name 0x%x (%s)", pos, debugStringIndex(name), name); + pos = writeAttrStrp(name, buffer, pos); + + /* + * now write references to all class layouts that implement this interface + */ + pos = writeInterfaceImplementors(context, interfaceClassEntry, buffer, pos); + pos = writeMethodDeclarations(context, interfaceClassEntry, buffer, pos); + + /* + * Write a terminating null attribute. + */ + return writeAttrNull(buffer, pos); + } + + private int writeInterfaceImplementors(DebugContext context, InterfaceClassEntry interfaceClassEntry, byte[] buffer, int p) { + return interfaceClassEntry.implementors().reduce(p, + (pos, classEntry) -> writeInterfaceImplementor(context, classEntry, buffer, pos), + (oldPos, newPos) -> newPos); + } + + private int writeInterfaceImplementor(DebugContext context, ClassEntry classEntry, byte[] buffer, int p) { + int pos = p; + log(context, " [0x%08x] interface implementor", pos); + int abbrevCode = DW_ABBREV_CODE_interface_implementor; + log(context, " [0x%08x] <2> Abbrev Number %d", pos, abbrevCode); + pos = writeAbbrevCode(abbrevCode, buffer, pos); + String name = uniqueDebugString("_" + classEntry.getTypeName()); + log(context, " [0x%08x] name 0x%x (%s)", pos, debugStringIndex(name), name); + pos = writeAttrStrp(name, buffer, pos); + int layoutOffset = getLayoutIndex(classEntry); + log(context, " [0x%08x] type 0x%x (%s)", pos, layoutOffset, classEntry.getTypeName()); + pos = writeAttrRefAddr(layoutOffset, buffer, pos); + int modifiers = Modifier.PUBLIC; + log(context, " [0x%08x] modifiers %s", pos, "public"); + pos = writeAttrAccessibility(modifiers, buffer, pos); + return pos; + } + + private int writeClassType(DebugContext context, ClassEntry classEntry, byte[] buffer, int p) { + int pos = p; + String name = classEntry.getTypeName(); + int pointerTypeOffset = pos; + + // define a pointer type referring to the underlying layout + setPointerIndex(classEntry, pos); + log(context, " [0x%08x] class pointer type", pos); + int abbrevCode = DW_ABBREV_CODE_class_pointer; + log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); + pos = writeAbbrevCode(abbrevCode, buffer, pos); + log(context, " [0x%08x] byte_size 0x%x", pos, 8); + pos = writeAttrData1((byte) 8, buffer, pos); + int layoutOffset = getLayoutIndex(classEntry); + log(context, " [0x%08x] type 0x%x", pos, layoutOffset); + pos = writeAttrRefAddr(layoutOffset, buffer, pos); + + // now write a typedef to name the pointer type and use it as the defining type + // for the Java class type name + setTypeIndex(classEntry, pos); + log(context, " [0x%08x] class pointer typedef", pos); + abbrevCode = DW_ABBREV_CODE_class_typedef; + log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); + pos = writeAbbrevCode(abbrevCode, buffer, pos); + log(context, " [0x%08x] name 0x%x (%s)", pos, debugStringIndex(name), name); + pos = writeAttrStrp(name, buffer, pos); + log(context, " [0x%08x] type (typedef) 0x%x (%s)", pos, pointerTypeOffset, name); + pos = writeAttrRefAddr(pointerTypeOffset, buffer, pos); + + return pos; + } + + private int writeInterfaceType(DebugContext context, InterfaceClassEntry interfaceClassEntry, byte[] buffer, int p) { + int pos = p; + String name = interfaceClassEntry.getTypeName(); + int pointerTypeOffset = pos; + + // define a pointer type referring to the underlying layout + setPointerIndex(interfaceClassEntry, pos); + log(context, " [0x%08x] interface pointer type", pos); + int abbrevCode = DW_ABBREV_CODE_interface_pointer; + log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); + pos = writeAbbrevCode(abbrevCode, buffer, pos); + log(context, " [0x%08x] byte_size 0x%x", pos, 8); + pos = writeAttrData1((byte) 8, buffer, pos); + int layoutOffset = getLayoutIndex(interfaceClassEntry); + log(context, " [0x%08x] type 0x%x", pos, layoutOffset); + pos = writeAttrRefAddr(layoutOffset, buffer, pos); + // now write a typedef to name the pointer type and use it as the defining type + // for the Java array type name + + setTypeIndex(interfaceClassEntry, pos); + log(context, " [0x%08x] interface pointer typedef", pos); + abbrevCode = DW_ABBREV_CODE_interface_typedef; + log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); + pos = writeAbbrevCode(abbrevCode, buffer, pos); + log(context, " [0x%08x] name 0x%x (%s)", pos, debugStringIndex(name), name); + pos = writeAttrStrp(name, buffer, pos); + log(context, " [0x%08x] type (typedef) 0x%x (%s)", pos, pointerTypeOffset, name); + pos = writeAttrRefAddr(pointerTypeOffset, buffer, pos); + + return pos; + } + + private int writeMethodLocations(DebugContext context, ClassEntry classEntry, byte[] buffer, int p) { + int pos = p; + LinkedList classPrimaryEntries = classEntry.getPrimaryEntries(); + for (PrimaryEntry primaryEntry : classPrimaryEntries) { + Range range = primaryEntry.getPrimary(); + if (!range.isDeoptTarget()) { + pos = writeMethodLocation(context, classEntry, range, buffer, pos); + } + } + + return pos; + } + + private int writeStaticFieldLocations(DebugContext context, ClassEntry classEntry, byte[] buffer, int p) { + // only write locations for static fields that have an offset greater than 0. + // a negative offset indicates that the field has been folded into code as + // an unmaterialized constant. + return classEntry.fields().filter(DwarfInfoSectionImpl::isManifestedStaticField).reduce(p, + (pos, fieldEntry) -> writeStaticFieldLocation(context, classEntry, fieldEntry, buffer, pos), + (oldPos, newPos) -> newPos); + } + + private static boolean isManifestedStaticField(FieldEntry fieldEntry) { + return Modifier.isStatic(fieldEntry.getModifiers()) && fieldEntry.getOffset() >= 0; + } + + private int writeStaticFieldLocation(DebugContext context, ClassEntry classEntry, FieldEntry fieldEntry, byte[] buffer, int p) { + int pos = p; + String fieldName = fieldEntry.fieldName(); + int fieldDefinitionOffset = getFieldDeclarationIndex(classEntry, fieldName); + log(context, " [0x%08x] static field location %s.%s", pos, classEntry.getTypeName(), fieldName); + int abbrevCode = DW_ABBREV_CODE_static_field_location; + log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); + pos = writeAbbrevCode(abbrevCode, buffer, pos); + log(context, " [0x%08x] specification 0x%x", pos, fieldDefinitionOffset); + pos = writeAttrRefAddr(fieldDefinitionOffset, buffer, pos); + // field offset needs to be relocated relative to static primitive or static object base + int offset = fieldEntry.getOffset(); + log(context, " [0x%08x] location heapbase + 0x%x (%s)", pos, offset, (fieldEntry.getValueType().isPrimitive() ? "primitive" : "object")); + pos = writeHeapLocation(offset, buffer, pos); + return pos; + } + + private int writeHeapLocation(int offset, byte[] buffer, int p) { + int pos = p; + if (dwarfSections.useHeapBase()) { + // write a location rebasing the offset relative to the heapbase register + byte regOp = (byte) (DW_OP_breg0 + dwarfSections.getHeapbaseRegister()); + // we have to size the DWARF expression by writing it to the scratch buffer + // so we can write its size as a ULEB before the expression itself + int size = putByte(regOp, scratch, 0) + putSLEB(offset, scratch, 0); + if (buffer == null) { + // add ULEB size to the expression size + return pos + putULEB(size, scratch, 0) + size; + } else { + // write the size and expression into the output buffer + pos = putULEB(size, buffer, pos); + pos = putByte(regOp, buffer, pos); + return putSLEB(offset, buffer, pos); + } + } else { + // write a relocatable address relative to the heap section start + byte regOp = DW_OP_addr; + int size = 9; + // write the size and expression into the output buffer + if (buffer == null) { + return pos + putULEB(size, scratch, 0) + size; + } else { + pos = putULEB(size, buffer, pos); + pos = putByte(regOp, buffer, pos); + return putRelocatableHeapOffset(offset, buffer, pos); + } + } + } + + private int writeArrayTypes(DebugContext context, byte[] buffer, int pos) { + log(context, " [0x%08x] array classes", pos); + return getTypes().filter(TypeEntry::isArray).reduce(pos, + (p, typeEntry) -> { + ArrayTypeEntry arrayTypeEntry = (ArrayTypeEntry) typeEntry; + return writeArrayTypeUnit(context, arrayTypeEntry, buffer, p); + }, + (oldpos, newpos) -> newpos); + } + + private int writeArrayTypeUnit(DebugContext context, ArrayTypeEntry arrayTypeEntry, byte[] buffer, int p) { + int pos = p; + int lengthPos = pos; + log(context, " [0x%08x] array class unit %s", pos, arrayTypeEntry.getTypeName()); + pos = writeCUHeader(buffer, pos); + assert pos == lengthPos + DW_DIE_HEADER_SIZE; + int abbrevCode = DW_ABBREV_CODE_array_unit; + 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"); + pos = writeAttrData1(DW_LANG_Java, buffer, pos); + String name = arrayTypeEntry.getTypeName(); + log(context, " [0x%08x] name 0x%x (%s)", pos, debugStringIndex(name), name); + pos = writeAttrStrp(name, buffer, pos); + // write the array layout and array reference DIEs + int layouIdx = pos; + pos = writeArrayLayout(context, arrayTypeEntry, buffer, pos); + pos = writeArrayType(context, arrayTypeEntry, layouIdx, buffer, pos); + /* + * Write a terminating null attribute. + */ + pos = writeAttrNull(buffer, pos); + + // fix up the CU length + patchLength(lengthPos, buffer, pos); + + return pos; + } + + private int writeArrayLayout(DebugContext context, ArrayTypeEntry arrayTypeEntry, byte[] buffer, int p) { + int pos = p; + TypeEntry elementType = arrayTypeEntry.getElementType(); + StructureTypeEntry headerType; + if (elementType.isPrimitive()) { + PrimitiveTypeEntry primitiveTypeEntry = (PrimitiveTypeEntry) elementType; + headerType = (StructureTypeEntry) lookupType(ARRAY_HEADER_STRUCT_NAME + primitiveTypeEntry.getTypeChar()); + } else { + headerType = (StructureTypeEntry) lookupType(ARRAY_HEADER_STRUCT_NAME + "A"); + } + log(context, " [0x%08x] array layout", pos); + int abbrevCode = DW_ABBREV_CODE_array_layout; + log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); + pos = writeAbbrevCode(abbrevCode, buffer, pos); + String name = uniqueDebugString("_" + arrayTypeEntry.getTypeName()); + log(context, " [0x%08x] name 0x%x (%s)", pos, debugStringIndex(name), name); + pos = writeAttrStrp(name, buffer, pos); + int size = headerType.getSize(); + log(context, " [0x%08x] byte_size 0x%x", pos, size); + pos = writeAttrData2((short) size, buffer, pos); + // now the child DIEs + // write a type definition for the element array field + int arrayDataTypeIdx = pos; + pos = writeArrayDataType(context, elementType, buffer, pos); + // write a zero length element array field + pos = writeArrayElementField(context, size, arrayDataTypeIdx, buffer, pos); + pos = writeArraySuperReference(context, arrayTypeEntry, buffer, pos); + /* + * Write a terminating null attribute. + */ + return writeAttrNull(buffer, pos); + } + + private int writeArrayDataType(DebugContext context, TypeEntry elementType, byte[] buffer, int p) { + int pos = p; + log(context, " [0x%08x] array element data type", pos); + int abbrevCode = DW_ABBREV_CODE_array_data_type; + log(context, " [0x%08x] <2> Abbrev Number %d", pos, abbrevCode); + pos = writeAbbrevCode(abbrevCode, buffer, pos); + int size = (elementType.isPrimitive() ? elementType.getSize() : 8); + log(context, " [0x%08x] byte_size 0x%x", pos, size); + pos = writeAttrData1((byte) size, buffer, pos); + String elementTypeName = elementType.getTypeName(); + int elementTypeIdx = getTypeIndex(elementTypeName); + log(context, " [0x%08x] type idx 0x%x (%s)", pos, elementTypeIdx, elementTypeName); + pos = writeAttrRefAddr(elementTypeIdx, buffer, pos); + return pos; + } + + private int writeArrayElementField(DebugContext context, int offset, int arrayDataTypeIdx, byte[] buffer, int p) { + int pos = p; + log(context, " [0x%08x] array element data field", pos); + int abbrevCode = DW_ABBREV_CODE_header_field; + log(context, " [0x%08x] <2> Abbrev Number %d", pos, abbrevCode); + pos = writeAbbrevCode(abbrevCode, buffer, pos); + String fieldName = uniqueDebugString("data"); + log(context, " [0x%08x] name 0x%x (%s)", pos, debugStringIndex(fieldName), fieldName); + pos = writeAttrStrp(fieldName, buffer, pos); + log(context, " [0x%08x] type idx 0x%x", pos, arrayDataTypeIdx); + pos = writeAttrRefAddr(arrayDataTypeIdx, buffer, pos); + int size = 0; + log(context, " [0x%08x] offset 0x%x (size 0x%x)", pos, offset, size); + pos = writeAttrData1((byte) offset, buffer, pos); + int modifiers = Modifier.PUBLIC; + log(context, " [0x%08x] modifiers %s", pos, "public"); + return writeAttrAccessibility(modifiers, buffer, pos); + + } + + private int writeArraySuperReference(DebugContext context, ArrayTypeEntry arrayTypeEntry, byte[] buffer, int pos) { + String headerName; + TypeEntry elementType = arrayTypeEntry.getElementType(); + if (elementType.isPrimitive()) { + headerName = ARRAY_HEADER_STRUCT_NAME + ((PrimitiveTypeEntry) elementType).getTypeChar(); + } else { + headerName = ARRAY_HEADER_STRUCT_NAME + "A"; + } + int headerTypeOffset = getTypeIndex(headerName); + log(context, " [0x%08x] super reference", pos); + int abbrevCode = DW_ABBREV_CODE_super_reference; + log(context, " [0x%08x] <2> Abbrev Number %d", pos, abbrevCode); + pos = writeAbbrevCode(abbrevCode, buffer, pos); + log(context, " [0x%08x] type 0x%x (%s)", pos, headerTypeOffset, headerName); + pos = writeAttrRefAddr(headerTypeOffset, buffer, pos); + // parent layout is embedded at start of object + log(context, " [0x%08x] data_member_location (super) 0x%x", pos, 0); + pos = writeAttrData1((byte) 0, buffer, pos); + log(context, " [0x%08x] modifiers public", pos); + int modifiers = Modifier.PUBLIC; + pos = writeAttrAccessibility(modifiers, buffer, pos); + return pos; + } + + private int writeArrayType(DebugContext context, ArrayTypeEntry arrayTypeEntry, int layoutOffset, byte[] buffer, int p) { + int pos = p; + int pointerTypeOffset = pos; + String name = uniqueDebugString(arrayTypeEntry.getTypeName()); + + // define a pointer type referring to the underlying layout + log(context, " [0x%08x] array pointer type", pos); + int abbrevCode = DW_ABBREV_CODE_array_pointer; + log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); + pos = writeAbbrevCode(abbrevCode, buffer, pos); + log(context, " [0x%08x] byte_size 0x%x", pos, 8); + pos = writeAttrData1((byte) 8, buffer, pos); + log(context, " [0x%08x] type (pointer) 0x%x (%s)", pos, layoutOffset, name); + pos = writeAttrRefAddr(layoutOffset, buffer, pos); + + // now write a typedef to name the pointer type and use it as the defining type + // for the Java array type name + setTypeIndex(arrayTypeEntry, pos); + log(context, " [0x%08x] array pointer typedef", pos); + abbrevCode = DW_ABBREV_CODE_array_typedef; + log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); + pos = writeAbbrevCode(abbrevCode, buffer, pos); + log(context, " [0x%08x] name 0x%x (%s)", pos, debugStringIndex(name), name); + pos = writeAttrStrp(name, buffer, pos); + log(context, " [0x%08x] type (typedef) 0x%x (%s)", pos, pointerTypeOffset, name); + pos = writeAttrRefAddr(pointerTypeOffset, buffer, pos); + + return pos; + } + + private int writeDeoptMethodsCU(DebugContext context, ClassEntry classEntry, byte[] buffer, int p) { + int pos = p; + assert classEntry.includesDeoptTarget(); + LinkedList classPrimaryEntries = classEntry.getPrimaryEntries(); + String fileName = classEntry.getFileName(); + int lineIndex = getLineIndex(classEntry); + int abbrevCode = (fileName.length() > 0 ? DW_ABBREV_CODE_class_unit1 : DW_ABBREV_CODE_class_unit2); + 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"); + pos = writeAttrData1(DW_LANG_Java, buffer, pos); + log(context, " [0x%08x] name 0x%x (%s)", pos, debugStringIndex(classEntry.getFileName()), classEntry.getFileName()); + pos = writeAttrStrp(classEntry.getFileName(), buffer, pos); + 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); + pos = writeAttrAddress(hi, buffer, pos); + if (abbrevCode == DW_ABBREV_CODE_class_unit1) { + log(context, " [0x%08x] stmt_list 0x%08x", pos, lineIndex); + pos = writeAttrData4(lineIndex, buffer, pos); + } + + for (PrimaryEntry primaryEntry : classPrimaryEntries) { + Range range = primaryEntry.getPrimary(); + if (range.isDeoptTarget()) { + pos = writeMethodLocation(context, classEntry, range, buffer, pos); + } + } + /* + * Write a terminating null attribute. + */ + return writeAttrNull(buffer, pos); + } + + 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 = 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); + /* + * Should pass true only if method is non-private. + */ + log(context, " [0x%08x] external true", pos); + pos = writeFlag(DW_FLAG_true, buffer, pos); + String methodKey = range.getFullMethodNameWithParamsAndReturnType(); + 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, false, buffer, pos); + /* + * Write a terminating null attribute. + */ + return writeAttrNull(buffer, pos); } private int writeCUHeader(byte[] buffer, int p) { @@ -196,7 +1165,7 @@ private int writeCUHeader(byte[] buffer, int p) { /* CU length. */ pos += putInt(0, scratch, 0); /* DWARF version. */ - pos += putShort(DW_VERSION_2, scratch, 0); + pos += putShort(DW_VERSION_4, scratch, 0); /* Abbrev offset. */ pos += putInt(0, scratch, 0); /* Address size. */ @@ -205,7 +1174,7 @@ private int writeCUHeader(byte[] buffer, int p) { /* CU length. */ pos = putInt(0, buffer, pos); /* DWARF version. */ - pos = putShort(DW_VERSION_2, buffer, pos); + pos = putShort(DW_VERSION_4, buffer, pos); /* Abbrev offset. */ pos = putInt(0, buffer, pos); /* Address size. */ @@ -252,60 +1221,6 @@ private static int findHi(LinkedList classPrimaryEntries, boolean return 0; } - private int writeCU(DebugContext context, ClassEntry classEntry, boolean isDeoptTargetCU, byte[] buffer, int p) { - int pos = p; - LinkedList classPrimaryEntries = classEntry.getPrimaryEntries(); - int lineIndex = classEntry.getLineIndex(); - int abbrevCode = (lineIndex >= 0 ? DW_ABBREV_CODE_compile_unit_1 : DW_ABBREV_CODE_compile_unit_2); - 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"); - pos = writeAttrData1(DW_LANG_Java, buffer, pos); - log(context, " [0x%08x] name 0x%x (%s)", pos, debugStringIndex(classEntry.getFileName()), classEntry.getFileName()); - pos = writeAttrStrp(classEntry.getFileName(), buffer, pos); - 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, isDeoptTargetCU); - int hi = findHi(classPrimaryEntries, classEntry.includesDeoptTarget(), isDeoptTargetCU); - 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); - pos = writeAttrAddress(hi, buffer, pos); - if (abbrevCode == DW_ABBREV_CODE_compile_unit_1) { - log(context, " [0x%08x] stmt_list 0x%08x", pos, lineIndex); - pos = writeAttrData4(lineIndex, buffer, pos); - } - for (PrimaryEntry primaryEntry : classPrimaryEntries) { - Range range = primaryEntry.getPrimary(); - if (isDeoptTargetCU == range.isDeoptTarget()) { - pos = writePrimary(context, range, buffer, pos); - } - } - /* - * Write a terminating null attribute for the the level 2 primaries. - */ - return writeAttrNull(buffer, pos); - - } - - private int writePrimary(DebugContext context, Range range, byte[] buffer, int p) { - int pos = p; - verboseLog(context, " [0x%08x] <1> Abbrev Number %d", pos, DW_ABBREV_CODE_subprogram); - pos = writeAbbrevCode(DW_ABBREV_CODE_subprogram, buffer, pos); - verboseLog(context, " [0x%08x] name 0x%X (%s)", pos, debugStringIndex(range.getFullMethodName()), range.getFullMethodName()); - pos = writeAttrStrp(range.getFullMethodName(), 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); - /* - * Need to pass true only if method is public. - */ - verboseLog(context, " [0x%08x] external true", pos); - return writeFlag(DW_FLAG_true, buffer, pos); - } - private int writeAttrStrp(String value, byte[] buffer, int p) { int pos = p; if (buffer == null) { @@ -326,10 +1241,25 @@ public int writeAttrString(String value, byte[] buffer, int p) { } } + public int writeAttrAccessibility(int modifiers, byte[] buffer, int p) { + byte access; + if (Modifier.isPublic(modifiers)) { + access = DW_ACCESS_public; + } else if (Modifier.isProtected(modifiers)) { + access = DW_ACCESS_protected; + } else if (Modifier.isPrivate(modifiers)) { + access = DW_ACCESS_private; + } else { + // package private -- make it public for now + access = DW_ACCESS_public; + } + return writeAttrData1(access, buffer, p); + } + /** - * The debug_info section content depends on abbrev section content and offset. + * The debug_info section depends on abbrev section. */ - private static final String TARGET_SECTION_NAME = DW_ABBREV_SECTION_NAME; + protected static final String TARGET_SECTION_NAME = TEXT_SECTION_NAME; @Override public String targetSectionName() { @@ -338,7 +1268,9 @@ public String targetSectionName() { private final LayoutDecision.Kind[] targetSectionKinds = { LayoutDecision.Kind.CONTENT, - LayoutDecision.Kind.OFFSET + LayoutDecision.Kind.SIZE, + /* Add this so we can use the text section base address for debug. */ + LayoutDecision.Kind.VADDR }; @Override diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLineSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLineSectionImpl.java index bc91db9188ee..1e804d314719 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 @@ -143,6 +143,8 @@ public String getSectionName() { @Override public void createContent() { + assert !contentByteArrayCreated(); + /* * We need to create a header, dir table, file table and line number table encoding for each * CU. @@ -155,15 +157,15 @@ public void createContent() { for (ClassEntry classEntry : getPrimaryClasses()) { if (classEntry.getFileName().length() != 0) { int startPos = pos; - classEntry.setLineIndex(startPos); + setLineIndex(classEntry, startPos); int headerSize = headerSize(); int dirTableSize = computeDirTableSize(classEntry); int fileTableSize = computeFileTableSize(classEntry); int prologueSize = headerSize + dirTableSize + fileTableSize; - classEntry.setLinePrologueSize(prologueSize); + setLinePrologueSize(classEntry, prologueSize); int lineNumberTableSize = computeLineNUmberTableSize(classEntry); int totalSize = prologueSize + lineNumberTableSize; - classEntry.setTotalSize(totalSize); + setLineSectionSize(classEntry, totalSize); pos += totalSize; } } @@ -280,6 +282,8 @@ public byte[] getOrDecideContent(Map alre @Override public void writeContent(DebugContext context) { + assert contentByteArrayCreated(); + byte[] buffer = getContent(); int pos = 0; @@ -289,7 +293,7 @@ public void writeContent(DebugContext context) { for (ClassEntry classEntry : getPrimaryClasses()) { if (classEntry.getFileName().length() != 0) { int startPos = pos; - assert classEntry.getLineIndex() == startPos; + assert getLineIndex(classEntry) == startPos; log(context, " [0x%08x] Compile Unit for %s", pos, classEntry.getFileName()); pos = writeHeader(classEntry, buffer, pos); log(context, " [0x%08x] headerSize = 0x%08x", pos, pos - startPos); @@ -313,7 +317,7 @@ private int writeHeader(ClassEntry classEntry, byte[] buffer, int p) { /* * 4 ubyte length field. */ - pos = putInt(classEntry.getTotalSize() - 4, buffer, pos); + pos = putInt(getLineSectionSize(classEntry) - 4, buffer, pos); /* * 2 ubyte version is always 2. */ @@ -321,7 +325,7 @@ private int writeHeader(ClassEntry classEntry, byte[] buffer, int p) { /* * 4 ubyte prologue length includes rest of header and dir + file table section. */ - int prologueSize = classEntry.getLinePrologueSize() - (4 + 2 + 4); + int prologueSize = getLinePrologueSize(classEntry) - (4 + 2 + 4); pos = putInt(prologueSize, buffer, pos); /* * 1 ubyte min instruction length is always 1. @@ -433,7 +437,7 @@ private int writeLineNumberTable(DebugContext context, ClassEntry classEntry, by * The primary file entry should always be first in the local files list. */ assert classEntry.localFilesIdx(fileEntry) == 1; - String primaryClassName = classEntry.getClassName(); + String primaryClassName = classEntry.getTypeName(); String primaryFileName = classEntry.getFileName(); String file = primaryFileName; int fileIdx = 1; @@ -441,7 +445,6 @@ private int writeLineNumberTable(DebugContext context, ClassEntry classEntry, by log(context, " [0x%08x] primary file %s", pos, primaryFileName); for (PrimaryEntry primaryEntry : classEntry.getPrimaryEntries()) { Range primaryRange = primaryEntry.getPrimary(); - assert primaryRange.getFileName().equals(primaryFileName); /* * Each primary represents a method i.e. a contiguous sequence of subranges. we assume * the default state at the start of each sequence because we always post an @@ -459,7 +462,7 @@ private int writeLineNumberTable(DebugContext context, ClassEntry classEntry, by /* * Set state for primary. */ - log(context, " [0x%08x] primary range [0x%08x, 0x%08x] %s:%d", pos, debugTextBase + primaryRange.getLo(), debugTextBase + primaryRange.getHi(), primaryRange.getFullMethodName(), + log(context, " [0x%08x] primary range [0x%08x, 0x%08x] %s:%d", pos, debugTextBase + primaryRange.getLo(), debugTextBase + primaryRange.getHi(), primaryRange.getFullMethodNameWithParams(), primaryRange.getLine()); /* @@ -511,7 +514,7 @@ private int writeLineNumberTable(DebugContext context, ClassEntry classEntry, by for (Range subrange : primaryEntry.getSubranges()) { assert subrange.getLo() >= primaryRange.getLo(); assert subrange.getHi() <= primaryRange.getHi(); - FileEntry subFileEntry = primaryEntry.getSubrangeFileEntry(subrange); + FileEntry subFileEntry = subrange.getFileEntry(); if (subFileEntry == null) { continue; } @@ -520,7 +523,7 @@ private int writeLineNumberTable(DebugContext context, ClassEntry classEntry, by long subLine = subrange.getLine(); long subAddressLo = subrange.getLo(); long subAddressHi = subrange.getHi(); - log(context, " [0x%08x] sub range [0x%08x, 0x%08x] %s:%d", pos, debugTextBase + subAddressLo, debugTextBase + subAddressHi, subrange.getFullMethodName(), subLine); + log(context, " [0x%08x] sub range [0x%08x, 0x%08x] %s:%d", pos, debugTextBase + subAddressLo, debugTextBase + subAddressHi, subrange.getFullMethodNameWithParams(), subLine); if (subLine < 0) { /* * No line info so stay at previous file:line. @@ -910,7 +913,7 @@ private static boolean isFixedAdvancePC(long addressDiff) { } /** - * The debug_line section content depends on debug_str section content and offset. + * The debug_line section depends on debug_str section. */ private static final String TARGET_SECTION_NAME = DW_STR_SECTION_NAME; @@ -921,7 +924,7 @@ public String targetSectionName() { private final LayoutDecision.Kind[] targetSectionKinds = { LayoutDecision.Kind.CONTENT, - LayoutDecision.Kind.OFFSET, + LayoutDecision.Kind.SIZE, }; @Override 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 4d3e5520f859..79299cd40f24 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSectionImpl.java @@ -32,6 +32,7 @@ import com.oracle.objectfile.LayoutDecisionMap; import com.oracle.objectfile.ObjectFile; import com.oracle.objectfile.debugentry.ClassEntry; +import com.oracle.objectfile.debugentry.TypeEntry; import com.oracle.objectfile.elf.ELFMachine; import com.oracle.objectfile.elf.ELFObjectFile; import org.graalvm.compiler.debug.DebugContext; @@ -39,7 +40,9 @@ import java.nio.ByteOrder; import java.util.Map; import java.util.Set; +import java.util.stream.Stream; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.HEAP_BEGIN_NAME; import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.TEXT_SECTION_NAME; /** @@ -78,6 +81,16 @@ public boolean isAArch64() { */ public abstract void writeContent(DebugContext debugContext); + /** + * Check whether the contents byte array has been sized and created. n.b. this does not imply + * that data has been written to the byte array. + * + * @return true if the contents byte array has been sized and created otherwise false. + */ + public boolean contentByteArrayCreated() { + return getContent() != null; + } + @Override public boolean isLoadable() { /* @@ -102,6 +115,8 @@ protected void enableLog(DebugContext context, int pos) { * to enable it during the second pass where the buffer gets written, but only if the scope * is enabled. */ + assert contentByteArrayCreated(); + if (context.areScopesEnabled()) { debug = true; debugBase = pos; @@ -197,6 +212,16 @@ protected int putRelocatableCodeOffset(long l, byte[] buffer, int p) { return pos; } + protected int putRelocatableHeapOffset(long l, byte[] buffer, int p) { + int pos = p; + /* + * Mark address so it is relocated relative to the start of the heap. + */ + markRelocationSite(pos, ObjectFile.RelocationKind.DIRECT_8, HEAP_BEGIN_NAME, false, Long.valueOf(l)); + pos = putLong(0, buffer, pos); + return pos; + } + protected int putULEB(long val, byte[] buffer, int p) { long l = val; int pos = p; @@ -311,6 +336,14 @@ protected int writeAttrData4(int value, byte[] buffer, int pos) { } } + protected int writeAttrData2(short value, byte[] buffer, int pos) { + if (buffer == null) { + return pos + putShort(value, scratch, 0); + } else { + return putShort(value, buffer, pos); + } + } + protected int writeAttrData1(byte value, byte[] buffer, int pos) { if (buffer == null) { return pos + putByte(value, scratch, 0); @@ -319,6 +352,15 @@ protected int writeAttrData1(byte value, byte[] buffer, int pos) { } } + public int writeAttrRefAddr(int value, byte[] buffer, int p) { + int pos = p; + if (buffer == null) { + return pos + putInt(0, scratch, 0); + } else { + return putInt(value, buffer, pos); + } + } + protected int writeAttrNull(byte[] buffer, int pos) { if (buffer == null) { return pos + putSLEB(0, scratch, 0); @@ -351,12 +393,21 @@ protected int writeAttrNull(byte[] buffer, int pos) { public abstract String getSectionName(); @Override - public byte[] getOrDecideContent(Map alreadyDecided, byte[] contentHint) { - /* - * Ensure content byte[] has been created before calling super method. - */ + public int getOrDecideSize(Map alreadyDecided, int sizeHint) { + + if (targetSectionName().startsWith(".debug")) { + ObjectFile.Element previousElement = this.getElement().getOwner().elementForName(targetSectionName()); + DwarfSectionImpl previousSection = (DwarfSectionImpl) previousElement.getImpl(); + assert previousSection.contentByteArrayCreated(); + } createContent(); + return getContent().length; + } + + @Override + public byte[] getOrDecideContent(Map alreadyDecided, byte[] contentHint) { + assert contentByteArrayCreated(); /* * Ensure content byte[] has been written before calling super method. * @@ -376,18 +427,22 @@ public Set getDependencies(Map getDependencies(Map getTypes() { + return dwarfSections.getTypes().stream(); + } + protected Iterable getPrimaryClasses() { return dwarfSections.getPrimaryClasses(); } protected int debugStringIndex(String str) { + if (!contentByteArrayCreated()) { + return 0; + } return dwarfSections.debugStringIndex(str); } + + protected String uniqueDebugString(String str) { + return dwarfSections.uniqueDebugString(str); + } + + protected TypeEntry lookupType(String typeName) { + return dwarfSections.lookupTypeEntry(typeName); + } + + protected int getTypeIndex(String typeName) { + if (!contentByteArrayCreated()) { + return 0; + } + return dwarfSections.getTypeIndex(typeName); + } + + protected void setTypeIndex(TypeEntry typeEntry, int pos) { + dwarfSections.setTypeIndex(typeEntry, pos); + } + + protected int getPointerIndex(String typeName) { + if (!contentByteArrayCreated()) { + return 0; + } + return dwarfSections.getPointerIndex(typeName); + } + + protected void setPointerIndex(ClassEntry classEntry, int pos) { + dwarfSections.setPointerIndex(classEntry, pos); + } + + protected int getCUIndex(ClassEntry classEntry) { + if (!contentByteArrayCreated()) { + return 0; + } + return dwarfSections.getCUIndex(classEntry); + } + + protected void setCUIndex(ClassEntry classEntry, int pos) { + dwarfSections.setCUIndex(classEntry, pos); + } + + protected int getDeoptCUIndex(ClassEntry classEntry) { + if (!contentByteArrayCreated()) { + return 0; + } + return dwarfSections.getDeoptCUIndex(classEntry); + } + + protected void setDeoptCUIndex(ClassEntry classEntry, int pos) { + dwarfSections.setDeoptCUIndex(classEntry, pos); + } + + protected int getLineIndex(ClassEntry classEntry) { + if (!contentByteArrayCreated()) { + return 0; + } + return dwarfSections.getLineIndex(classEntry); + } + + protected void setLineIndex(ClassEntry classEntry, int pos) { + dwarfSections.setLineIndex(classEntry, pos); + } + + protected int getLineSectionSize(ClassEntry classEntry) { + if (!contentByteArrayCreated()) { + return 0; + } + return dwarfSections.getLineSectionSize(classEntry); + } + + protected void setLineSectionSize(ClassEntry classEntry, int pos) { + dwarfSections.setLineSectionSize(classEntry, pos); + } + + protected int getLinePrologueSize(ClassEntry classEntry) { + if (!contentByteArrayCreated()) { + return 0; + } + return dwarfSections.getLinePrologueSize(classEntry); + } + + protected void setLinePrologueSize(ClassEntry classEntry, int pos) { + dwarfSections.setLinePrologueSize(classEntry, pos); + } + + protected int getLayoutIndex(ClassEntry classEntry) { + if (!contentByteArrayCreated()) { + return 0; + } + return dwarfSections.getLayoutIndex(classEntry); + } + + protected void setLayoutIndex(ClassEntry classEntry, int pos) { + dwarfSections.setLayoutIndex(classEntry, pos); + } + + protected void setFieldDeclarationIndex(ClassEntry classEntry, String fieldName, int pos) { + dwarfSections.setFieldDeclarationIndex(classEntry, fieldName, pos); + } + + protected int getFieldDeclarationIndex(ClassEntry classEntry, String fieldName) { + if (!contentByteArrayCreated()) { + return 0; + } + return dwarfSections.getFieldDeclarationIndex(classEntry, fieldName); + } + + protected void setMethodDeclarationIndex(ClassEntry classEntry, String methodName, int pos) { + dwarfSections.setMethodDeclarationIndex(classEntry, methodName, pos); + } + + protected int getMethodDeclarationIndex(ClassEntry classEntry, String methodName) { + if (!contentByteArrayCreated()) { + return 0; + } + return dwarfSections.getMethodDeclarationIndex(classEntry, methodName); + } } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfStrSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfStrSectionImpl.java index ee68bcb71050..c7ed628fc771 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfStrSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfStrSectionImpl.java @@ -30,8 +30,8 @@ import com.oracle.objectfile.debugentry.StringEntry; import org.graalvm.compiler.debug.DebugContext; +import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_INFO_SECTION_NAME; import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_STR_SECTION_NAME; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.TEXT_SECTION_NAME; /** * Generator for debug_str section. @@ -48,6 +48,8 @@ public String getSectionName() { @Override public void createContent() { + assert !contentByteArrayCreated(); + int pos = 0; for (StringEntry stringEntry : dwarfSections.getStringTable()) { if (stringEntry.isAddToStrSection()) { @@ -62,40 +64,39 @@ public void createContent() { @Override public void writeContent(DebugContext context) { + assert contentByteArrayCreated(); + byte[] buffer = getContent(); int size = buffer.length; int pos = 0; enableLog(context, pos); + verboseLog(context, " [0x%08x] DEBUG_STR", pos); for (StringEntry stringEntry : dwarfSections.getStringTable()) { if (stringEntry.isAddToStrSection()) { assert stringEntry.getOffset() == pos; String string = stringEntry.getString(); pos = putAsciiStringBytes(string, buffer, pos); + verboseLog(context, " [0x%08x] string = %s", pos, string); } } assert pos == size; } /** - * The debug_str section content depends on text section content and offset. + * The debug_str section depends on info section. */ - private static final String TARGET_SECTION_NAME = TEXT_SECTION_NAME; + private static final String TARGET_SECTION_NAME = DW_INFO_SECTION_NAME; @Override public String targetSectionName() { return TARGET_SECTION_NAME; } - /** - * The debug_str section content depends on text section content and offset. - */ private final LayoutDecision.Kind[] targetSectionKinds = { LayoutDecision.Kind.CONTENT, - LayoutDecision.Kind.OFFSET, - /* Add this so we can use the text section base address for debug. */ - LayoutDecision.Kind.VADDR, + LayoutDecision.Kind.SIZE, }; @Override diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVLineRecordBuilder.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVLineRecordBuilder.java index 1156522fb8e3..29a245d904fe 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 @@ -82,7 +82,7 @@ CVLineRecord build(PrimaryEntry entry) { */ private void processRange(Range range) { - FileEntry file = cvDebugInfo.findFile(range.getFileAsPath()); + FileEntry file = range.getFileEntry(); if (file == null) { debug("processRange: range has no file: %s\n", range); return; diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVSymbolSubsectionBuilder.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVSymbolSubsectionBuilder.java index f0eac5086e0c..a0217addb97d 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVSymbolSubsectionBuilder.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/pecoff/cv/CVSymbolSubsectionBuilder.java @@ -136,7 +136,7 @@ private String getDebuggerName(Range range) { final String methodName; if (noMainFound && range.getMethodName().equals("main")) { noMainFound = false; - methodName = range.getClassAndMethodName(); + methodName = range.getFullMethodName(); } else { /* In the future, use a more user-friendly name instead of a hash function. */ methodName = range.getSymbolName(); 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 915eac731684..28b343621e0c 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java @@ -28,6 +28,7 @@ import static com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFrameSizeChange.Type.CONTRACT; import static com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFrameSizeChange.Type.EXTEND; +import java.lang.reflect.Modifier; import java.nio.file.Path; import java.util.Arrays; import java.util.LinkedList; @@ -36,15 +37,23 @@ import java.util.stream.Stream; import com.oracle.graal.pointsto.infrastructure.OriginalClassProvider; +import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.objectfile.debuginfo.DebugInfoProvider; +import com.oracle.svm.core.StaticFieldsSupport; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.config.ConfigurationValues; import com.oracle.svm.core.config.ObjectLayout; import com.oracle.svm.core.graal.code.SubstrateBackend; +import com.oracle.svm.core.image.ImageHeapPartition; +import com.oracle.svm.hosted.annotation.CustomSubstitutionMethod; +import com.oracle.svm.hosted.annotation.CustomSubstitutionType; +import com.oracle.svm.hosted.image.NativeImageHeap.ObjectInfo; import com.oracle.svm.hosted.image.sources.SourceManager; +import com.oracle.svm.hosted.lambda.LambdaSubstitutionType; import com.oracle.svm.hosted.meta.HostedArrayClass; +import com.oracle.svm.hosted.meta.HostedClass; import com.oracle.svm.hosted.meta.HostedField; import com.oracle.svm.hosted.meta.HostedMethod; import com.oracle.svm.hosted.meta.HostedType; @@ -52,36 +61,69 @@ import com.oracle.svm.hosted.meta.HostedInterface; import com.oracle.svm.hosted.meta.HostedPrimitiveType; +import com.oracle.svm.hosted.substitute.InjectedFieldsType; +import com.oracle.svm.hosted.substitute.SubstitutionField; +import com.oracle.svm.hosted.substitute.SubstitutionMethod; +import com.oracle.svm.hosted.substitute.SubstitutionType; +import jdk.vm.ci.meta.ResolvedJavaField; import org.graalvm.compiler.code.CompilationResult; import org.graalvm.compiler.code.SourceMapping; +import org.graalvm.compiler.core.common.CompressEncoding; import org.graalvm.compiler.debug.DebugContext; import org.graalvm.compiler.graph.NodeSourcePosition; import org.graalvm.nativeimage.ImageSingletons; - +import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.LineNumberTable; import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; +import jdk.vm.ci.meta.Signature; /** * Implementation of the DebugInfoProvider API interface that allows type, code and heap data info * to be passed to an ObjectFile when generation of debug info is enabled. */ class NativeImageDebugInfoProvider implements DebugInfoProvider { + private static final JavaKind[] ARRAY_KINDS = {JavaKind.Boolean, JavaKind.Byte, JavaKind.Char, JavaKind.Short, JavaKind.Int, JavaKind.Float, JavaKind.Long, JavaKind.Double, JavaKind.Object}; + private final DebugContext debugContext; private final NativeImageCodeCache codeCache; @SuppressWarnings("unused") private final NativeImageHeap heap; + boolean useHeapBase; + int heapShift; + int primitiveStartOffset; + int referenceStartOffset; NativeImageDebugInfoProvider(DebugContext debugContext, NativeImageCodeCache codeCache, NativeImageHeap heap) { super(); this.debugContext = debugContext; this.codeCache = codeCache; this.heap = heap; + ObjectInfo primitiveFields = heap.getObjectInfo(StaticFieldsSupport.getStaticPrimitiveFields()); + ObjectInfo objectFields = heap.getObjectInfo(StaticFieldsSupport.getStaticObjectFields()); + if (SubstrateOptions.SpawnIsolates.getValue()) { + CompressEncoding compressEncoding = ImageSingletons.lookup(CompressEncoding.class); + this.useHeapBase = compressEncoding.hasBase(); + this.heapShift = (compressEncoding.hasShift() ? compressEncoding.getShift() : 0); + } else { + this.useHeapBase = false; + this.heapShift = 0; + } + // offsets need to be adjusted relative to the heap base plus partition-specific offset + primitiveStartOffset = (int) primitiveFields.getOffset(); + referenceStartOffset = (int) objectFields.getOffset(); + } + + @Override + public boolean useHeapBase() { + return useHeapBase; } @Override public Stream typeInfoProvider() { - return heap.getUniverse().getTypes().stream().map(this::createDebugTypeInfo); + Stream headerTypeInfo = computeHeaderTypeInfo(); + Stream heapTypeInfo = heap.getUniverse().getTypes().stream().map(this::createDebugTypeInfo); + return Stream.concat(headerTypeInfo, heapTypeInfo); } @Override @@ -91,52 +133,384 @@ public Stream codeInfoProvider() { @Override public Stream dataInfoProvider() { - return Stream.empty(); + return heap.getObjects().stream().filter(this::acceptObjectInfo).map(this::createDebugDataInfo); } static ObjectLayout OBJECTLAYOUT = ConfigurationValues.getObjectLayout(); - private abstract class NativeImageDebugTypeInfo implements DebugTypeInfo { + /* + * HostedType wraps an AnalysisType and both HostedType and AnalysisType punt calls to + * getSourceFilename to the wrapped class so for consistency we need to do type names and path + * lookup relative to the doubly unwrapped HostedType. + * + * However, note that the result of the unwrap on the AnalysisType may be a SubstitutionType + * which wraps both an original type and the annotated type that substitutes it. Unwrapping + * normally returns the AnnotatedType which we need to use to resolve the file name. However, we + * need to use the original to name the owning type to ensure that names found in method param + * and return types resolve correctly. + */ + protected static ResolvedJavaType getJavaType(HostedType hostedType, boolean wantOriginal) { + ResolvedJavaType javaType; + if (wantOriginal) { + // check for wholesale replacement of the original class + javaType = hostedType.getWrapped().getWrappedWithoutResolve(); + if (javaType instanceof SubstitutionType) { + return ((SubstitutionType) javaType).getOriginal(); + } else if (javaType instanceof CustomSubstitutionType) { + return ((CustomSubstitutionType) javaType).getOriginal(); + } else if (javaType instanceof LambdaSubstitutionType) { + return ((LambdaSubstitutionType) javaType).getOriginal(); + } else if (javaType instanceof InjectedFieldsType) { + return ((InjectedFieldsType) javaType).getOriginal(); + } else { + return javaType; + } + } + return hostedType.getWrapped().getWrapped(); + } - protected final HostedType hostedType; - protected final ResolvedJavaType javaType; - protected final Class clazz; + protected static ResolvedJavaType getJavaType(HostedMethod hostedMethod, boolean wantOriginal) { + if (wantOriginal) { + // check for wholesale replacement of the original class + HostedType hostedType = hostedMethod.getDeclaringClass(); + ResolvedJavaType javaType = hostedType.getWrapped().getWrappedWithoutResolve(); + if (javaType instanceof SubstitutionType) { + return ((SubstitutionType) javaType).getOriginal(); + } else if (javaType instanceof CustomSubstitutionType) { + return ((CustomSubstitutionType) javaType).getOriginal(); + } else if (javaType instanceof LambdaSubstitutionType) { + return ((LambdaSubstitutionType) javaType).getOriginal(); + } else if (javaType instanceof InjectedFieldsType) { + return ((InjectedFieldsType) javaType).getOriginal(); + } + // check for replacement of the original method only + ResolvedJavaMethod javaMethod = hostedMethod.getWrapped().getWrapped(); + if (javaMethod instanceof SubstitutionMethod) { + javaMethod = ((SubstitutionMethod) javaMethod).getOriginal(); + } else if (javaMethod instanceof CustomSubstitutionMethod) { + javaMethod = ((CustomSubstitutionMethod) javaMethod).getOriginal(); + } + return javaMethod.getDeclaringClass(); + } + ResolvedJavaMethod javaMethod = hostedMethod.getWrapped().getWrapped(); + return javaMethod.getDeclaringClass(); + } - protected NativeImageDebugTypeInfo(HostedType hostedType) { - this.hostedType = hostedType; - this.javaType = hostedType.getWrapped(); + protected static ResolvedJavaType getJavaType(HostedField hostedField, boolean wantOriginal) { + if (wantOriginal) { + // check for wholesale replacement of the original class + HostedType hostedType = hostedField.getDeclaringClass(); + ResolvedJavaType javaType = hostedType.getWrapped().getWrappedWithoutResolve(); + if (javaType instanceof SubstitutionType) { + return ((SubstitutionType) javaType).getOriginal(); + } else if (javaType instanceof CustomSubstitutionType) { + return ((CustomSubstitutionType) javaType).getOriginal(); + } else if (javaType instanceof LambdaSubstitutionType) { + return ((LambdaSubstitutionType) javaType).getOriginal(); + } else if (javaType instanceof InjectedFieldsType) { + return ((InjectedFieldsType) javaType).getOriginal(); + } + // check for replacement of the original field only + ResolvedJavaField javaField = hostedField.wrapped.wrapped; + if (javaField instanceof SubstitutionField) { + javaField = ((SubstitutionField) javaField).getOriginal(); + } + return javaField.getDeclaringClass(); + } + ResolvedJavaField javaField = hostedField.wrapped.wrapped; + return javaField.getDeclaringClass(); + } + + private static final Path cachePath = SubstrateOptions.getDebugInfoSourceCacheRoot(); + + private abstract class NativeImageDebugFileInfo implements DebugFileInfo { + private Path fullFilePath; + + @SuppressWarnings("try") + NativeImageDebugFileInfo(HostedType hostedType) { + ResolvedJavaType javaType = getJavaType(hostedType, false); + Class clazz; if (hostedType instanceof OriginalClassProvider) { clazz = ((OriginalClassProvider) hostedType).getJavaClass(); } else { clazz = null; } + SourceManager sourceManager = ImageSingletons.lookup(SourceManager.class); + try (DebugContext.Scope s = debugContext.scope("DebugFileInfo", hostedType)) { + fullFilePath = sourceManager.findAndCacheSource(javaType, clazz, debugContext); + } catch (Throwable e) { + throw debugContext.handle(e); + } + } + + @SuppressWarnings("try") + NativeImageDebugFileInfo(HostedMethod hostedMethod) { + ResolvedJavaType javaType = getJavaType(hostedMethod, false); + HostedType hostedType = hostedMethod.getDeclaringClass(); + Class clazz; + if (hostedType instanceof OriginalClassProvider) { + clazz = ((OriginalClassProvider) hostedType).getJavaClass(); + } else { + clazz = null; + } + SourceManager sourceManager = ImageSingletons.lookup(SourceManager.class); + try (DebugContext.Scope s = debugContext.scope("DebugFileInfo", hostedType)) { + fullFilePath = sourceManager.findAndCacheSource(javaType, clazz, debugContext); + } catch (Throwable e) { + throw debugContext.handle(e); + } + } + + @SuppressWarnings("try") + NativeImageDebugFileInfo(HostedField hostedField) { + ResolvedJavaType javaType = getJavaType(hostedField, false); + HostedType hostedType = hostedField.getDeclaringClass(); + Class clazz; + if (hostedType instanceof OriginalClassProvider) { + clazz = ((OriginalClassProvider) hostedType).getJavaClass(); + } else { + clazz = null; + } + SourceManager sourceManager = ImageSingletons.lookup(SourceManager.class); + try (DebugContext.Scope s = debugContext.scope("DebugFileInfo", hostedType)) { + fullFilePath = sourceManager.findAndCacheSource(javaType, clazz, debugContext); + } catch (Throwable e) { + throw debugContext.handle(e); + } + } + + @Override + public String fileName() { + if (fullFilePath != null) { + Path filename = fullFilePath.getFileName(); + if (filename != null) { + return filename.toString(); + } + } + return ""; + } + + @Override + public Path filePath() { + if (fullFilePath != null) { + return fullFilePath.getParent(); + } + return null; + } + + @Override + public Path cachePath() { + return cachePath; + } + } + + private abstract class NativeImageDebugTypeInfo extends NativeImageDebugFileInfo implements DebugTypeInfo { + protected final HostedType hostedType; + + @SuppressWarnings("try") + protected NativeImageDebugTypeInfo(HostedType hostedType) { + super(hostedType); + this.hostedType = hostedType; } @SuppressWarnings("try") @Override public void debugContext(Consumer action) { - try (DebugContext.Scope s = debugContext.scope("DebugTypeInfo", typeName())) { + try (DebugContext.Scope s = debugContext.scope("DebugTypeInfo", typeName())) { action.accept(debugContext); } catch (Throwable e) { throw debugContext.handle(e); } } + public String toJavaName(HostedType hostedType) { + return getJavaType(hostedType, true).toJavaName(); + } + @Override public String typeName() { - return hostedType.toJavaName(); + return toJavaName(hostedType); } @Override public int size() { if (hostedType instanceof HostedInstanceClass) { + // We know the actual instance size in bytes. return ((HostedInstanceClass) hostedType).getInstanceSize(); + } else if (hostedType instanceof HostedArrayClass) { + // Use the size of header common to all arrays of this type. + return OBJECTLAYOUT.getArrayBaseOffset(hostedType.getComponentType().getStorageKind()); + } else if (hostedType instanceof HostedInterface) { + // Use the size of the header common to all implementors. + return OBJECTLAYOUT.getFirstFieldOffset(); } else { - return hostedType.getStorageKind().getByteCount(); + // Use the number of bytes needed needed to store the value. + assert hostedType instanceof HostedPrimitiveType; + JavaKind javaKind = hostedType.getStorageKind(); + return (javaKind == JavaKind.Void ? 0 : javaKind.getByteCount()); } } } + private class NativeImageHeaderTypeInfo implements DebugHeaderTypeInfo { + String typeName; + int size; + JavaKind elementKind; + List fieldInfos; + + NativeImageHeaderTypeInfo(String typeName, int size, JavaKind baseKind) { + this.typeName = typeName; + this.size = size; + this.elementKind = baseKind; + this.fieldInfos = new LinkedList<>(); + } + + void addField(String name, String valueType, int offset, int size) { + NativeImageDebugHeaderFieldInfo fieldinfo = new NativeImageDebugHeaderFieldInfo(name, typeName, valueType, offset, size); + fieldInfos.add(fieldinfo); + } + + @SuppressWarnings("try") + @Override + public void debugContext(Consumer action) { + try (DebugContext.Scope s = debugContext.scope("DebugTypeInfo", typeName())) { + action.accept(debugContext); + } catch (Throwable e) { + throw debugContext.handle(e); + } + } + + @Override + public String typeName() { + return typeName; + } + + @Override + public DebugTypeKind typeKind() { + return DebugTypeKind.HEADER; + } + + @Override + public String fileName() { + return ""; + } + + @Override + public Path filePath() { + return null; + } + + @Override + public Path cachePath() { + return null; + } + + @Override + public int size() { + return size; + } + + @Override + public Stream fieldInfoProvider() { + return fieldInfos.stream(); + } + + 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) { + this.name = name; + this.ownerType = ownerType; + this.valueType = valueType; + this.offset = offset; + this.size = size; + this.modifiers = Modifier.PUBLIC; + } + + @Override + public String name() { + return name; + } + + @Override + public String ownerType() { + return ownerType; + } + + @Override + public String valueType() { + return valueType; + } + + @Override + public int offset() { + return offset; + } + + @Override + public int size() { + return size; + } + + @Override + public int modifiers() { + return modifiers; + } + + @Override + public String fileName() { + return ""; + } + + @Override + public Path filePath() { + return null; + } + + @Override + public Path cachePath() { + return null; + } + } + } + + private Stream computeHeaderTypeInfo() { + List infos = new LinkedList<>(); + int hubOffset = OBJECTLAYOUT.getHubOffset(); + int referenceSize = OBJECTLAYOUT.getReferenceSize(); + int hubFieldSize = referenceSize; + String hubTypeName = "java.lang.Class"; + int arrayLengthOffset = OBJECTLAYOUT.getArrayLengthOffset(); + int arrayLengthSize = OBJECTLAYOUT.sizeInBytes(JavaKind.Int); + int arrayIdHashOffset = OBJECTLAYOUT.getArrayIdentityHashcodeOffset(); + int arrayIdHashSize = OBJECTLAYOUT.sizeInBytes(JavaKind.Int); + int objHeaderSize = OBJECTLAYOUT.getFirstFieldOffset(); + // we need array headers for all Java kinds + + NativeImageHeaderTypeInfo objHeader = new NativeImageHeaderTypeInfo("_objhdr", objHeaderSize, null); + objHeader.addField("hub", hubTypeName, hubOffset, hubFieldSize); + infos.add(objHeader); + + // create a header for each + for (JavaKind arrayKind : ARRAY_KINDS) { + String name = "_arrhdr" + arrayKind.getTypeChar(); + int headerSize = OBJECTLAYOUT.getArrayBaseOffset(arrayKind); + NativeImageHeaderTypeInfo arrHeader = new NativeImageHeaderTypeInfo(name, headerSize, arrayKind); + arrHeader.addField("hub", hubTypeName, hubOffset, hubFieldSize); + arrHeader.addField("len", "int", arrayLengthOffset, arrayLengthSize); + if (arrayIdHashOffset > 0) { + arrHeader.addField("idHash", "int", arrayIdHashOffset, arrayIdHashSize); + } + infos.add(arrHeader); + } + return infos.stream(); + } + private class NativeImageDebugEnumTypeInfo extends NativeImageDebugInstanceTypeInfo implements DebugEnumTypeInfo { HostedInstanceClass enumClass; @@ -163,22 +537,68 @@ public DebugTypeKind typeKind() { public int headerSize() { return OBJECTLAYOUT.getFirstFieldOffset(); } + @Override public Stream fieldInfoProvider() { - return Arrays.stream(hostedType.getInstanceFields(true)).map(this::createDebugFieldInfo); + Stream instanceFieldsStream = Arrays.stream(hostedType.getInstanceFields(false)).map(this::createDebugFieldInfo); + if (hostedType instanceof HostedInstanceClass && hostedType.getStaticFields().length > 0) { + Stream staticFieldsStream = Arrays.stream(hostedType.getStaticFields()).map(this::createDebugStaticFieldInfo); + return Stream.concat(instanceFieldsStream, staticFieldsStream); + } else { + return instanceFieldsStream; + } + } + + @Override + public Stream methodInfoProvider() { + return Arrays.stream(hostedType.getAllDeclaredMethods()).map(this::createDebugMethodInfo); + } + + @Override + public String superName() { + HostedClass superClass = hostedType.getSuperclass(); + /* + * HostedType wraps an AnalysisType and both HostedType and AnalysisType punt calls to + * getSourceFilename to the wrapped class so for consistency we need to do the path + * lookup relative to the doubly unwrapped HostedType. + */ + if (superClass != null) { + return getJavaType(superClass, true).toJavaName(); + } + return null; + } + + @Override + public Stream interfaces() { + final NativeImageDebugTypeInfo typeInfo = this; + return Arrays.stream(hostedType.getInterfaces()).map(this::toJavaName); } protected NativeImageDebugFieldInfo createDebugFieldInfo(HostedField field) { return new NativeImageDebugFieldInfo(field); } - protected class NativeImageDebugFieldInfo implements DebugFieldInfo { + protected NativeImageDebugFieldInfo createDebugStaticFieldInfo(ResolvedJavaField field) { + return new NativeImageDebugFieldInfo((HostedField) field); + } + + protected NativeImageDebugMethodInfo createDebugMethodInfo(HostedMethod method) { + return new NativeImageDebugMethodInfo(method); + } + + protected class NativeImageDebugFieldInfo extends NativeImageDebugFileInfo implements DebugFieldInfo { private final HostedField field; NativeImageDebugFieldInfo(HostedField field) { + super(field); this.field = field; } + @Override + public String name() { + return field.getName(); + } + @Override public String ownerType() { return typeName(); @@ -186,23 +606,109 @@ public String ownerType() { @Override public String valueType() { - return field.getType().toJavaName(); + HostedType valueType = field.getType(); + return toJavaName(valueType); } @Override public int offset() { - return field.getLocation(); + int offset = field.getLocation(); + // for static fields we need to add in the appropriate partition base + // but only if we have a real offset + if (isStatic() && offset >= 0) { + if (isPrimitive()) { + offset += primitiveStartOffset; + } else { + offset += referenceStartOffset; + } + } + return offset; } @Override public int size() { return OBJECTLAYOUT.sizeInBytes(field.getType().getStorageKind()); } + + @Override + public int modifiers() { + return field.getModifiers(); + } + + private boolean isStatic() { + return Modifier.isStatic(modifiers()); + } + + private boolean isPrimitive() { + return field.getType().getStorageKind().isPrimitive(); + } + } + + protected class NativeImageDebugMethodInfo extends NativeImageDebugFileInfo implements DebugMethodInfo { + private final HostedMethod hostedMethod; + + NativeImageDebugMethodInfo(HostedMethod hostedMethod) { + super(hostedMethod); + this.hostedMethod = hostedMethod; + } + + @Override + public String name() { + String name = hostedMethod.format("%n"); + if ("".equals(name)) { + ResolvedJavaMethod unwrapped = hostedMethod.getWrapped().getWrapped(); + if (unwrapped instanceof SubstitutionMethod) { + unwrapped = ((SubstitutionMethod) unwrapped).getOriginal(); + } + name = unwrapped.format("%h"); + if (name.indexOf('$') >= 0) { + name = name.substring(name.lastIndexOf('$') + 1); + } + } + return name; + } + + @Override + public String ownerType() { + return typeName(); + } + + @Override + public String valueType() { + return hostedMethod.getSignature().getReturnType(null).toJavaName(); + } + + @Override + public List paramTypes() { + LinkedList paramTypes = new LinkedList<>(); + Signature signature = hostedMethod.getSignature(); + for (int i = 0; i < signature.getParameterCount(false); i++) { + paramTypes.add(signature.getParameterType(i, null).toJavaName()); + } + return paramTypes; + } + + @Override + public List paramNames() { + // can only provide blank names for now + LinkedList paramNames = new LinkedList<>(); + Signature signature = hostedMethod.getSignature(); + for (int i = 0; i < signature.getParameterCount(false); i++) { + paramNames.add(""); + } + return paramNames; + } + + @Override + public int modifiers() { + return hostedMethod.getModifiers(); + } } } - private class NativeImageDebugInterfaceTypeInfo extends NativeImageDebugTypeInfo implements DebugInterfaceTypeInfo { + private class NativeImageDebugInterfaceTypeInfo extends NativeImageDebugInstanceTypeInfo implements DebugInterfaceTypeInfo { HostedInterface interfaceClass; + NativeImageDebugInterfaceTypeInfo(HostedInterface interfaceClass) { super(interfaceClass); this.interfaceClass = interfaceClass; @@ -237,7 +743,8 @@ public int lengthOffset() { @Override public String elementType() { - return arrayClass.getComponentType().toJavaName(); + HostedType elementType = arrayClass.getComponentType(); + return toJavaName(elementType); } } @@ -255,7 +762,13 @@ public DebugTypeKind typeKind() { @Override public int bitCount() { - return primitiveType.getStorageKind().getBitCount(); + JavaKind javaKind = primitiveType.getStorageKind(); + return (javaKind == JavaKind.Void ? 0 : javaKind.getBitCount()); + } + + @Override + public char typeChar() { + return primitiveType.getStorageKind().getTypeChar(); } @Override @@ -265,21 +778,17 @@ public int flags() { case 'B': case 'S': case 'I': - case 'J': - { + case 'J': { return FLAG_NUMERIC | FLAG_INTEGRAL | FLAG_SIGNED; } - case 'C': - { + case 'C': { return FLAG_NUMERIC | FLAG_INTEGRAL; } case 'F': - case 'D': - { + case 'D': { return FLAG_NUMERIC; } - default: - { + default: { assert typeChar == 'V' || typeChar == 'Z'; return 0; } @@ -307,106 +816,77 @@ private NativeImageDebugTypeInfo createDebugTypeInfo(HostedType hostedType) { * Implementation of the DebugCodeInfo API interface that allows code info to be passed to an * ObjectFile when generation of debug info is enabled. */ - private class NativeImageDebugCodeInfo implements DebugCodeInfo { - private final HostedMethod method; - private final ResolvedJavaType javaType; + private class NativeImageDebugCodeInfo extends NativeImageDebugFileInfo implements DebugCodeInfo { + private final HostedMethod hostedMethod; private final CompilationResult compilation; - private Path fullFilePath; - private final Path cachePath; @SuppressWarnings("try") NativeImageDebugCodeInfo(HostedMethod method, CompilationResult compilation) { - this.method = method; - HostedType declaringClass = method.getDeclaringClass(); - Class clazz = declaringClass.getJavaClass(); - /* - * HostedType wraps an AnalysisType and both HostedType and AnalysisType punt calls to - * getSourceFilename to the wrapped class so for consistency we need to do the path - * lookup relative to the doubly unwrapped HostedType. - */ - this.javaType = declaringClass.getWrapped().getWrapped(); + super(method); + this.hostedMethod = method; this.compilation = compilation; - this.cachePath = SubstrateOptions.getDebugInfoSourceCacheRoot(); - SourceManager sourceManager = ImageSingletons.lookup(SourceManager.class); - try (DebugContext.Scope s = debugContext.scope("DebugCodeInfo", declaringClass)) { - fullFilePath = sourceManager.findAndCacheSource(javaType, clazz, debugContext); - } catch (Throwable e) { - throw debugContext.handle(e); - } } @SuppressWarnings("try") @Override public void debugContext(Consumer action) { - try (DebugContext.Scope s = debugContext.scope("DebugCodeInfo", method)) { + try (DebugContext.Scope s = debugContext.scope("DebugCodeInfo", hostedMethod)) { action.accept(debugContext); } catch (Throwable e) { throw debugContext.handle(e); } } - @Override - public String fileName() { - if (fullFilePath != null) { - Path filename = fullFilePath.getFileName(); - if (filename != null) { - return filename.toString(); - } - } - return ""; - } - - @Override - public Path filePath() { - if (fullFilePath != null) { - return fullFilePath.getParent(); - } - return null; - } - - @Override - public Path cachePath() { - return cachePath; - } - @Override public String className() { - return javaType.toClassName(); + return getJavaType(hostedMethod, true).toJavaName(); } @Override public String methodName() { - return method.format("%n"); + ResolvedJavaMethod targetMethod = hostedMethod.getWrapped().getWrapped(); + if (targetMethod instanceof SubstitutionMethod) { + targetMethod = ((SubstitutionMethod) targetMethod).getOriginal(); + } else if (targetMethod instanceof CustomSubstitutionMethod) { + targetMethod = ((CustomSubstitutionMethod) targetMethod).getOriginal(); + } + String name = targetMethod.getName(); + if (name.equals("")) { + name = targetMethod.format("%h"); + if (name.indexOf('$') >= 0) { + name = name.substring(name.lastIndexOf('$') + 1); + } + } + return name; } @Override public String symbolNameForMethod() { - return NativeBootImage.localSymbolNameForMethod(method); + return NativeBootImage.localSymbolNameForMethod(hostedMethod); } - @Override - public String paramNames() { - return method.format("%P"); + public String paramSignature() { + return hostedMethod.format("%P"); } @Override public String returnTypeName() { - return method.format("%R"); + return hostedMethod.format("%R"); } @Override public int addressLo() { - return method.getCodeAddressOffset(); + return hostedMethod.getCodeAddressOffset(); } @Override public int addressHi() { - return method.getCodeAddressOffset() + compilation.getTargetCodeSize(); + return hostedMethod.getCodeAddressOffset() + compilation.getTargetCodeSize(); } @Override public int line() { - LineNumberTable lineNumberTable = method.getLineNumberTable(); + LineNumberTable lineNumberTable = hostedMethod.getLineNumberTable(); if (lineNumberTable != null) { return lineNumberTable.getLineNumber(0); } @@ -454,6 +934,10 @@ public List getFrameSizeChanges() { public boolean isDeoptTarget() { return methodName().endsWith(HostedMethod.METHOD_NAME_DEOPT_SUFFIX); } + + public int getModifiers() { + return hostedMethod.getModifiers(); + } } /** @@ -510,7 +994,26 @@ public String className() { @Override public String methodName() { - return method.format("%n"); + ResolvedJavaMethod targetMethod = method; + if (targetMethod instanceof HostedMethod) { + targetMethod = ((HostedMethod) targetMethod).getWrapped(); + } + if (targetMethod instanceof AnalysisMethod) { + targetMethod = ((AnalysisMethod) targetMethod).getWrapped(); + } + if (targetMethod instanceof SubstitutionMethod) { + targetMethod = ((SubstitutionMethod) targetMethod).getOriginal(); + } else if (targetMethod instanceof CustomSubstitutionMethod) { + targetMethod = ((CustomSubstitutionMethod) targetMethod).getOriginal(); + } + String name = targetMethod.getName(); + if (name.equals("")) { + name = targetMethod.format("%h"); + if (name.indexOf('$') >= 0) { + name = name.substring(name.lastIndexOf('$') + 1); + } + } + return name; } @Override @@ -545,14 +1048,9 @@ private void computeFullFilePath() { clazz = ((OriginalClassProvider) declaringClass).getJavaClass(); } /* -<<<<<<< HEAD * HostedType wraps an AnalysisType and both HostedType and AnalysisType punt calls to * getSourceFilename to the wrapped class so for consistency we need to do the path * lookup relative to the doubly unwrapped HostedType or singly unwrapped AnalysisType. -======= - * HostedType and HostedType punt calls to getSourceFilename to the wrapped class so - * for consistency we need to do the path lookup relative to the wrapped class. ->>>>>>> e5e46875909... basic cut of DebugTypeInfo interface and implementation */ if (declaringClass instanceof HostedType) { declaringClass = ((HostedType) declaringClass).getWrapped(); @@ -593,4 +1091,81 @@ public Type getType() { return type; } } + + private class NativeImageDebugDataInfo implements DebugDataInfo { + HostedClass hostedClass; + ImageHeapPartition partition; + long offset; + long address; + long size; + ResolvedJavaType javaType; + Class clazz; + String typeName; + String provenance; + + @SuppressWarnings("try") + @Override + public void debugContext(Consumer action) { + try (DebugContext.Scope s = debugContext.scope("DebugCodeInfo", provenance)) { + action.accept(debugContext); + } catch (Throwable e) { + throw debugContext.handle(e); + } + } + + NativeImageDebugDataInfo(ObjectInfo objectInfo) { + hostedClass = objectInfo.getClazz(); + partition = objectInfo.getPartition(); + offset = objectInfo.getOffset(); + address = objectInfo.getAddress(); + size = objectInfo.getSize(); + provenance = objectInfo.toString(); + /* + * HostedType wraps an AnalysisType and both HostedType and AnalysisType punt calls to + * getSourceFilename to the wrapped class so for consistency we need to do the type name + * and path lookup relative to the doubly unwrapped HostedType. + */ + javaType = hostedClass.getWrapped().getWrappedWithoutResolve(); + if (hostedClass instanceof OriginalClassProvider) { + clazz = ((OriginalClassProvider) hostedClass).getJavaClass(); + } else { + clazz = null; + } + typeName = hostedClass.toJavaName(); + } + + // accessors + public String getProvenance() { + return provenance; + } + + public String getTypeName() { + return typeName; + } + + public String getPartition() { + return partition.getName() + "{" + partition.getSize() + "}@" + partition.getStartOffset(); + } + + public long getOffset() { + return offset; + } + + public long getAddress() { + return address; + } + + public long getSize() { + return size; + } + } + + private boolean acceptObjectInfo(ObjectInfo objectInfo) { + // this rejects filler partition objects + return (objectInfo.getPartition().getStartOffset() > 0); + } + + private DebugDataInfo createDebugDataInfo(ObjectInfo objectInfo) { + return new NativeImageDebugDataInfo(objectInfo); + } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/SubstitutionField.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/SubstitutionField.java index ddc8b0bba786..b6eb5025734a 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/SubstitutionField.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/SubstitutionField.java @@ -70,6 +70,14 @@ public JavaConstant readValue(JavaConstant receiver) { return value; } + public ResolvedJavaField getOriginal() { + return original; + } + + public ResolvedJavaField getAnnotated() { + return annotated; + } + @Override public int getModifiers() { return annotated.getModifiers(); From 9c5f2150ac559e086f3f72140060821ab99f6554 Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Fri, 20 Nov 2020 14:05:41 +0000 Subject: [PATCH 3/6] Switch C++ mapping to use Java names for base types and pointers for oops ensure gdb can decode object references to addresses style fixes rebase and correct object header layout style fixes fix debuginfotest script eclipse style fixes disable test of object layout printing if gdb version less than 10 correct pattern for gdb version match corrections after zakkak review eclipse style fixes fix index key duplication when a stub and the method it is derived from both get included with -O0 fix error in lookup of original class for methods and fields fix related problem with constructor method names use gdb from env var GDB_BIN if available Fix typos and out of date content simplify code add check for printing of statci fields avoid numerous static imports of constants style fixes add details of currently missing features --- substratevm/DebugInfo.md | 372 +++++--- substratevm/mx.substratevm/mx_substratevm.py | 2 +- substratevm/mx.substratevm/testhello.py | 230 +++-- .../objectfile/debugentry/ArrayTypeEntry.java | 2 +- .../objectfile/debugentry/ClassEntry.java | 107 +-- .../objectfile/debugentry/DebugInfoBase.java | 57 +- .../objectfile/debugentry/FileEntry.java | 2 +- .../objectfile/debugentry/MethodEntry.java | 4 +- .../objectfile/debugentry/PrimaryEntry.java | 5 - .../oracle/objectfile/debugentry/Range.java | 11 - .../debugentry/StructureTypeEntry.java | 10 +- .../objectfile/debugentry/TypeEntry.java | 4 +- .../debuginfo/DebugInfoProvider.java | 8 +- .../elf/dwarf/DwarfARangesSectionImpl.java | 15 +- .../elf/dwarf/DwarfAbbrevSectionImpl.java | 867 ++++++++---------- .../objectfile/elf/dwarf/DwarfDebugInfo.java | 91 +- .../elf/dwarf/DwarfFrameSectionImpl.java | 55 +- .../dwarf/DwarfFrameSectionImplAArch64.java | 2 +- .../elf/dwarf/DwarfInfoSectionImpl.java | 523 ++++++----- .../elf/dwarf/DwarfLineSectionImpl.java | 10 +- .../elf/dwarf/DwarfSectionImpl.java | 24 +- .../elf/dwarf/DwarfStrSectionImpl.java | 7 +- .../image/NativeImageDebugInfoProvider.java | 239 ++--- 23 files changed, 1341 insertions(+), 1306 deletions(-) diff --git a/substratevm/DebugInfo.md b/substratevm/DebugInfo.md index efae00fa9981..e94195177cee 100644 --- a/substratevm/DebugInfo.md +++ b/substratevm/DebugInfo.md @@ -12,11 +12,7 @@ The resulting image should contain code (method) debug records in a format the GNU Debugger (GDB) understands (Windows support is still under development). At present it makes no difference which positive value is supplied to the `GenerateDebugInfo` option. -Note that it is currently recommended to pass flag `-H:-UseIsolates` on the command -line whenever debug info generation is enabled. When isolates are enabled field -values of Object (reference) type are stored as offsets from the heap base rather -than direct addresses and this confuses the debugger when it tries to read through -object fields. This problem should be corrected in a later release. +## Source File Caching The `GenerateDebugInfo` option also enables caching of sources for any JDK runtime classes, GraalVM classes, and application classes which can @@ -90,6 +86,77 @@ _apps/target/hello.jar_ and _apps/target/greeter.jar_ will be used to derive the default search roots _apps/target/hello-sources.jar_ and _apps/target/greeter-sources.jar_. +## Debugging with Isolates + +Note that it is currently recommended to disable use of Isolates by +passing flag `-H:-UseIsolates` on the command line when debug info +generation is enabled. Enabling of Isolates affects the way that oops +(object references) are encoded. In turn that means the debug info +generator has to provide gdb with information about how to translate +an encoded oop to the address in memory where the object data is +stored. This sometimes requires care when asking gdb to process +encoded oops vs decoded raw addresses. + +When isolates are disabled oops are essentially raw addresses pointing +directly at the object contents. This is the same whether the oop is +stored in a static/instance field or has been loaded into a register. + +When an oop is stored in a static or instance field gdb knows the type +of the value stored in the field and knows how to dereference it to +locate the object contents. For example, assume we have a `Units` type +that details the scale used for a blueprint drawing and that the +`Units` instance has a `String` field called `print_name`. Assume also +we have a static field `DEFAULT_UNIT` that holds the standard `Units` +instance. The following command will print the name for the default +units. + +``` +(gdb) print *com.acme.Blueprint::DEFAULT_UNIT->print_name +``` + +gdb knows the type of the oop stored in `Blueprint::DEFAULT_UNIT` and +knows how to dereference it to locate the object field +values. Likewise, it knows that the `print_name` field is a `String` +it will translate the oop stored in that field to an address where the +String contents are stored and it will print the values of the +`String` instance fields one by one. + +If, say, an oop referring to the `print_name` String has been loaded +into $rdx it is still possible to print it using a straightforward +cast to the pointer type that gdb associates with oop references. + +``` +(gdb) print/x *('java.lang.String' *)$rdx +``` + +The raw address in the register is the same as the oop value stored +in the field. + +By contrast, when isolates are enabled oop references stored in static +or instance fields are actually relative addresses, offsets from a +dedicated heap base register (r14 on x86_64, r29 on AArch64), rather +than direct addresses. However, when an oop gets loaded during +execution it is almost always immediately converted to a direct +address by adding the offset to the heap base register value. The +DWARF info encoded into the image tells gdb to rebase object pointers +whenever it tries to dereference them to access the underlying object +data. + +This still means gdb will do the right thing when it accesses an +object via a static field. When processing the field expression above +that prints the default unit name gdb will automatically rebase the +`Units` oop stored in field `DEFAULT_UNIT` by adding it to the heap +base register. It will then fetch and rebase the oop stored in its +`print_name` field to access the contents of the `String`. + +However, this transformation won't work correctly in the second case +where gdb is passed an oop that has already been loaded into a +register and converted to a pointer. It is necessary to restore the +original oop by reverting it back to an offset: + +``` +(gdb) print/x *('java.lang.String' *)($rdx - $r14) +``` ## Currently Implemented Features @@ -109,138 +176,189 @@ line number info for inlined code, including inlined GraalVM methods. So, GDB may switch files even though you are still in the same compiled method. +### Currently Missing Features + + - reference by name to values boudn to parameter and local vars + +This feature is scheduled for inclusion in a later release. + ### Special considerations for debugging Java from GDB GDB does not currently include support for debugging of Java programs. In consequence, debug capability has been implemented by generating debug info that models the Java program as an equivalent C++ program. Java -class, array and interface references are modelled as pointers to -underlying C++ (class/struct) layout types. - -So, for example in the DWARF debug info model `java.lang.String` names -a pointer type to a class (layout) type named `_java.lang.String`. The -layout type declares fields like `hash` of type `int` and `value` of -type `byte[]` and methods like `String(byte[])`, `charAt(int)`, -etc. It also inherits fields and methods from class (layout) type -_java.lang.Object using C++ public inheritance. The latter in turn +class, array and interface references are actually pointers to records +that contain the relevant field/array data. In the corresponding C++ +model the Java name is used to label the underlying C++ (class/struct) +layout types and Java references appear as pointers. + +So, for example in the DWARF debug info model `java.lang.String` +identifies a C++ class. This class layout type declares the expected +fields like `hash` of type `int` and `value` of type `byte[]` and +methods like `String(byte[])`, `charAt(int)`, etc. However, the copy +constructor which appears in Java as `String(String)` appears in gdb +with the signature `String(java.lang.String *)`. + +The C++ layout class inherits fields and methods from class (layout) +type java.lang.Object using C++ public inheritance. The latter in turn inherits standard oop header fields from a special struct class named -_objhdr which includes a pointer to the object's class. +_objhdr which includes a single field called `hub` whose type is +`java.lang.Class *` i.e. it is a pointer to the object's class. The ptype command can be used to print details of a specific type. Note that the java type name must be specified in quotes because to escape the embedded `.` characters. ``` -(gdb) ptype -ptype 'java.lang.String' -type = class _java.lang.String : public _java.lang.Object { +(gdb) ptype 'java.lang.String' +type = class java.lang.String : public java.lang.Object { private: - byte [] value; + byte [] *value; int hash; byte coder; + public: - void String(byte []); + void String(byte [] *); + void String(char [] *); + void String(byte [] *, java.lang.String *); . . . char charAt(int); . . . -} * + java.lang.String * concat(java.lang.String *); + . . . +} ``` The print command can be used to print the contents of a referenced object field by field. Note how a cast is used to convert a raw memory address to -a reference of a specific Java type. +a reference for a specific Java type. ``` -(gdb) print/x *('java.lang.String')0x7ffff7c01058 +(gdb) print *('java.lang.String' *) 0x7ffff7c01060 $1 = { - <_java.lang.Object> = { + = { <_objhdr> = { - hub = 0x90c670 + hub = 0x90cb58 }, }, - members of _java.lang.String: - value = 0x7ffff7c01198, - hash = 0x0, - coder = 0x0 + members of java.lang.String: + value = 0x7ffff7c011a0, + hash = 0, + coder = 0 '\000' } ``` -The hub field in the object header is actually a reference of type -`java.lang/Class`. Note that the field is typed using the pointer type -rather than the underlying layout type `_java.lang.Class`. +The hub field in the object header is actually a reference of Java type +`java.lang.Class`. Note that the field is typed by gdb using a pointer +to the underlying C++ class (layout) type. ``` (gdb) ptype _objhdr type = struct _objhdr { - java.lang.Class hub; + java.lang.Class *hub; } ``` -Given a putative object reference it is possible to identify it's type by -printing the contents of the String referenced from the hub's name field. -First the value is cast to an object reference. Then a path expression is -used to dereference through the the hub field and the hub's name field to -the `byte[]` value array located in the name String. +Given an address that might be an object reference it is possible to +verify that case and identify the object's type by printing the +contents of the String referenced from the hub's name field. First +the value is cast to an object reference. Then a path expression is +used to dereference through the the hub field and the hub's name field +to the `byte[]` value array located in the name String. ``` (gdb) print/x ((_objhdr *)$rdi) -$19 = 0x7ffff7c01028 -(gdb) print *$19->hub->name->value -$21 = { +$2 = 0x7ffff7c01028 +(gdb) print *$2->hub->name->value +$3 = { <_arrhdrB> = { - hub = 0x90e0a8, + hub = 0x90e4b8, len = 19, - idHash = 797697923 - }, - members of _byte []: - data = 0x9046d8 "[Ljava.lang.String;" + idHash = 3759493 + }, + members of byte []: + data = 0x904798 "[Ljava.lang.String;" } ``` -A simpler equivalent is as follows - -``` -(gdb) x/s $19->hub->name->value->data -0x9046d8: "[Ljava.lang.String;" -``` - The value in register rdx is obviously a reference to a String array. Casting it to this type shows it has length 1. ``` -(gdb) print *('java.lang.String[]')$rdi -$40 = { +(gdb) print *('java.lang.String[]' *)$rdi +$4 = { <_arrhdrA> = { - hub = 0x906b30, - len = 1, + hub = 0x906a78, + len = 2, idHash = 0 - }, - members of _java.lang.String[]: + }, + members of java.lang.String[]: data = 0x7ffff7c01038 } +``` +A simpler command which allows just the name of the hub object to be +printed is as follows: + +``` +(gdb) x/s $2->hub->name->value->data +798: "[Ljava.lang.String;" ``` -Note that attempting to print the hub name for an invalid reference -will fail. +Indeed it is useful to define a gdb command `hubname` to execute this +operation on an arbitrary input argument + +``` +command hubname + x/s ((_objhdr *)($arg0))->hub->name->value->data +end + +(gdb) hubname $2 +0x904798: "[Ljava.lang.String;" +``` + +Notice that the `hubname` command also masks out the low 3 flag bits in +the hub field that may sometimes get set by the runtime during program operation. + +Attempting to print the hub name for an invalid reference will fail +safe, printing an error message. ``` (gdb) p/x (_objhdr *)$rdx -$37 = 0x2 -(gdb) x/s $37->hub->name->value->data +$5 = 0x2 +(gdb) hubname $rdx Cannot access memory at address 0x2 ``` -Array type layouts are modelled with an array header whose fields -include the hub, array length, idhash plus type-specific padding -followed by a (zero length) array inlined into the object. The common -prefix for the array header and object header structs means that they -are all able to be viewed as oops. +Array type layouts are modelled with a class. The class inherits +fields from an array header struct specific to the array element type, +one of _arrhdrZ _arrhdrB, _arrhdrS, ... _arrhdrA (the last one is for +object arrays). Inherited fields include the hub, array length, idHash +to round up the header size to a boundary suitable for the array +element type. The array class (layout) type includes only one field, a +C++ array of length zero whose element type is a primtiive type or +Java referece type. ``` (gdb) ptype 'java.lang.String[]' -type = struct _java.lang.String[] : public _arrhdrA { - java.lang.String data[0]; -} * +type = struct java.lang.String[] : public _arrhdrA { + java.lang.String *data[0]; +} +``` + +Notice that the type of the values stored in the data array is +`java.lang.String *` i.e. the C++ array stores Java references +i.e. addresss as far as the C++ model is concerned. + +The array header structs are all extensions of the basic _objhdr type +which means that arrays and objects can both be safely cast to oops. + +``` +(gdb) ptype _arrhdrA +type = struct _arrhdrA { + java.lang.Class *hub; + int len; + int idHash; +} ``` Interfaces layouts are modelled as union types whose members are the @@ -254,55 +372,63 @@ type = union _java.lang.CharSequence { _java.lang.StringBuilder _java.lang.StringBuilder; _java.lang.String _java.lang.String; _java.nio.CharBuffer _java.nio.CharBuffer; -} * +} ``` Given a reference typed to an interface it can be resolved to the -relevant class type by viewing it through the relevant union element -``` -(gdb) print (('java.lang.String[]')$rdi)->data[0] -$41 = (java.lang.String) 0x7ffff7c01058 -(gdb) print ('java.lang.CharSequence')$41 -$42 = (java.lang.CharSequence) 0x7ffff7c01058 -(gdb) x/s ((_objhdr*)$42)->hub->name->value->data -0x7d97e0: "java.lang.String\250", -(gdb) print $42->'_java.lang.String' -$45 = { - <_java.lang.Object> = { +relevant class type by viewing it through the relevant union element. + +If we take the first String in the args array we can ask gdb to cast +it to interface CharSequence +``` +(gdb) print (('java.lang.String[]' *)$rdi)->data[0] +$6 = (java.lang.String) 0x7ffff7c01060 +(gdb) print ('java.lang.CharSequence')$6 +$7 = (java.lang.CharSequence) 0x7ffff7c01060 +``` + +The hubname command can be used to identify the actual type of the +object that implements this interface and that type name can be used +to select the union element used to print the object. + +``` +(gdb) hubname $7 +0x7d96d8: "java.lang.String\270", +(gdb) print $7->'_java.lang.String' +$18 = { + = { <_objhdr> = { - hub = 0x90c670 - }, }, - members of _java.lang.String: - value = 0x7ffff7c01198, + hub = 0x90cb58 + }, }, + members of java.lang.String: + value = 0x7ffff7c011a0, hash = 0, coder = 0 '\000' } ``` The current debug info model does not include the location info needed -to allow symbolic names for local vars and parameter vars to be resolved -to primitive values or object references. However, the debugger does -understand method names and static field names. +to allow symbolic names for local vars and parameter vars to be +resolved to primitive values or object references. However, the +debugger does understand method names and static field names. The following command places a breakpoint on the main entry point for -class `Hello`. Note that the GDB considers the method to belong to the -class (layout) type `_Hello` rather than the pointer type `Hello`. Also, -since GDB thinks this is a C++ program it uses the `::` separator to -separate the method name from the class name. +class `Hello`. Note that since GDB thinks this is a C++ method it uses +the `::` separator to separate the method name from the class name. ``` (gdb) info func ::main All functions matching regular expression "::main": File Hello.java: - void _Hello::main(java.lang.String[]); -(gdb) b _Hello::main + void Hello::main(java.lang.String[] *); +(gdb) x/4i Hello::main +=> 0x4065a0 : sub $0x8,%rsp + 0x4065a4 : cmp 0x8(%r15),%rsp + 0x4065a8 : jbe 0x4065fd + 0x4065ae : callq 0x406050 +(gdb) b Hello::main Breakpoint 1 at 0x4065a0: file Hello.java, line 43. -(gdb) x/4i _Hello::main - 0x4065a0 <_Hello::main(java.lang.String[])>: sub $0x8,%rsp - 0x4065a4 <_Hello::main(java.lang.String[])+4>: cmp 0x8(%r15),%rsp - 0x4065a8 <_Hello::main(java.lang.String[])+8>: jbe 0x4065fd <_Hello::main(java.lang.String[])+93> - 0x4065ae <_Hello::main(java.lang.String[])+14>: callq 0x406050 <_Hello$Greeter::greeter(java.lang.String[])> ``` An example of a static field containing Object data is provided by @@ -320,12 +446,16 @@ type = class _java.math.BigInteger : public _java.lang.Number { int firstNonzeroIntNumPlusTwo; static java.math.BigInteger[][] powerCache; . . . -} * + public: + void BigInteger(byte [] *); + void BigInteger(java.lang.String *, int); + . . . +} (gdb) info var powerCache All variables matching regular expression "powerCache": File java/math/BigInteger.java: - java.math.BigInteger[][] _java.math.BigInteger::powerCache; + java.math.BigInteger[][] *java.math.BigInteger::powerCache; ``` The static variable name can be used to refer to the value stored in @@ -333,18 +463,18 @@ this field. Note also that the address operator can be used identify the location (address) of the field in the heap. ``` -(gdb) p '_java.math.BigInteger'::powerCache -$56 = (java.math.BigInteger[][]) 0xa6fd98 -(gdb) p &'_java.math.BigInteger'::powerCache -$55 = (java.math.BigInteger[][] *) 0xa6fbc8 +(gdb) p 'java.math.BigInteger'::powerCache +$9 = (java.math.BigInteger[][] *) 0xa6fd98 +(gdb) p &'java.math.BigInteger'::powerCache +$10 = (java.math.BigInteger[][] **) 0xa6fbd8 ``` gdb dereferences through symbolic names for static fields to access the primitive value or object stored in the field ``` -(gdb) p *'_java.math.BigInteger'::powerCache -$33 = { +(gdb) p *'java.math.BigInteger'::powerCache +$11 = { <_arrhdrA> = { hub = 0x9ab3d0, len = 37, @@ -353,26 +483,26 @@ $33 = { members of _java.math.BigInteger[][]: data = 0xa6fda8 } -(gdb) p '_java.math.BigInteger'::powerCache->data[0]@4 -$51 = {0x0, 0x0, 0xc09378, 0xc09360} -(gdb) p *'_java.math.BigInteger'::powerCache->data[2] -$52 = { +(gdb) p 'java.math.BigInteger'::powerCache->data[0]@4 +$12 = {0x0, 0x0, 0xc09378, 0xc09360} +(gdb) p *'java.math.BigInteger'::powerCache->data[2] +$13 = { <_arrhdrA> = { hub = 0x919898, len = 1, idHash = 1796421813 }, - members of _java.math.BigInteger[]: + members of java.math.BigInteger[]: data = 0xc09388 } -(gdb) p *'_java.math.BigInteger'::powerCache->data[2]->data[0] -$53 = { - <_java.lang.Number> = { - <_java.lang.Object> = { +(gdb) p *'java.math.BigInteger'::powerCache->data[2]->data[0] +$14 = { + = { + = { <_objhdr> = { - hub = 0x919c70 - }, }, }, - members of _java.math.BigInteger: + hub = 0x919bc8 + }, }, }, + members of java.math.BigInteger: mag = 0xa5b030, signum = 1, bitLengthPlusOne = 0, diff --git a/substratevm/mx.substratevm/mx_substratevm.py b/substratevm/mx.substratevm/mx_substratevm.py index 52a92d14dc6d..ebffb1425e23 100644 --- a/substratevm/mx.substratevm/mx_substratevm.py +++ b/substratevm/mx.substratevm/mx_substratevm.py @@ -763,7 +763,7 @@ def _debuginfotest(native_image, path, build_only, args): native_image(native_image_args) if mx.get_os() == 'linux' and not build_only: - mx.run(['gdb', '-x', join(parent, 'mx.substratevm/testhello.py'), join(path, 'hello.hello')]) + mx.run([os.environ.get('GDB_BIN', 'gdb'), '-x', join(parent, 'mx.substratevm/testhello.py'), join(path, 'hello.hello')]) def _javac_image(native_image, path, args=None): diff --git a/substratevm/mx.substratevm/testhello.py b/substratevm/mx.substratevm/testhello.py index 21d51b4d7b27..155f36bca9e7 100644 --- a/substratevm/mx.substratevm/testhello.py +++ b/substratevm/mx.substratevm/testhello.py @@ -130,13 +130,30 @@ def test(): package_file_pattern = '[a-zA-Z0-9_/]+\\.java' varname_pattern = '[a-zA-Z0-9_]+' wildcard_pattern = '.*' + # obtain the gdb version + # n.b. we can only test printing in gdb 10.1 upwards + exec_string=execute("show version") + checker = Checker('show version', + r"GNU gdb %s (%s)\.(%s)%s"%(wildcard_pattern, digits_pattern, digits_pattern, wildcard_pattern)) + matches = checker.check(exec_string, skip_fails=False) + # n.b. can only get back here with one match + match = matches[0] + major = int(match.group(1)) + # may need this if 8.x and 9.x get patched + # minor = int(match.group(2)) + # printing object data requires gdb 10+ + can_print_data = major > 9 + + if not can_print_data: + print("Warning: cannot test printing of objects!") + # disable prompting to continue output execute("set pagination off") # 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." - exec_string = execute("break _hello.Hello::main") + # expect "Breakpoint 1 at 0x[0-9a-f]+: file hello.Hello.java, line 67." + exec_string = execute("break hello.Hello::main") rexp = r"Breakpoint 1 at %s: file hello/Hello\.java, line 67\."%address_pattern checker = Checker('break main', rexp) checker.check(exec_string) @@ -151,73 +168,87 @@ def test(): 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:67" # 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%s_hello\.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"#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) ]) checker.check(exec_string, skip_fails=False) - # print the contents of the arguments array which will be in rdi - exec_string = execute("print /x *(('java.lang.String[]')$rdi)") - checker = Checker("print String[] args", - [r"%s = {"%(wildcard_pattern), - r"%s<_arrhdrA> = {"%(spaces_pattern), - r"%shub = %s,"%(spaces_pattern, address_pattern), - r"%slen = 0x0,"%(spaces_pattern), - r"%sidHash = %s"%(spaces_pattern, address_pattern), - r"%s},"%(spaces_pattern), - r"%smembers of _java\.lang\.String\[\]:"%(spaces_pattern), - r"%sdata = %s"%(spaces_pattern, address_pattern), - "}"]) - - checker.check(exec_string, skip_fails=False) - - # print the hub of the array and check it has a name field - exec_string = execute("print /x *(('java.lang.String[]')$rdi)->hub") - checker = Checker("print String[] hub", - [r"%s = {"%(wildcard_pattern), - r"%s<_java.lang.Object> = {"%(spaces_pattern), - r"%s<_objhdr> = {"%(spaces_pattern), - r"%shub = %s"%(spaces_pattern, address_pattern), - r"%s}, },"%(spaces_pattern), - r"%smembers of _java\.lang\.Class:"%(spaces_pattern), - r"%sname = %s"%(spaces_pattern, address_pattern), - "}"]) - - checker.check(exec_string, skip_fails=True) - - # print the hub name field and check it is String[] - # n.b. the expected String text is not necessarily null terminated - # so we need a wild card before the final quote - exec_string = execute("x/s (('java.lang.String[]')$rdi)->hub->name->value->data") - checker = Checker("print String[] hub name", - r"%s:%s\"\[Ljava.lang.String;.*\""%(address_pattern, spaces_pattern)) - checker.check(exec_string, skip_fails=False) + if can_print_data: + # print the contents of the arguments array which will be in rdi + exec_string = execute("print /x *(('java.lang.String[]' *)$rdi)") + checker = Checker("print String[] args", + [r"%s = {"%(wildcard_pattern), + r"%s<_arrhdrA> = {"%(spaces_pattern), + r"%shub = %s,"%(spaces_pattern, address_pattern), + r"%sidHash = %s,"%(spaces_pattern, address_pattern), + r"%slen = 0x0"%(spaces_pattern), + r"%s},"%(spaces_pattern), + r"%smembers of java\.lang\.String\[\]:"%(spaces_pattern), + r"%sdata = %s"%(spaces_pattern, address_pattern), + "}"]) + + checker.check(exec_string, skip_fails=False) + + # print the hub of the array and check it has a name field + exec_string = execute("print /x *(('java.lang.String[]' *)$rdi)->hub") + checker = Checker("print String[] hub", + [r"%s = {"%(wildcard_pattern), + r"%s = {"%(spaces_pattern), + r"%s<_objhdr> = {"%(spaces_pattern), + r"%shub = %s,"%(spaces_pattern, address_pattern), + r"%sidHash = %s"%(spaces_pattern, address_pattern), + r"%s}, },"%(spaces_pattern), + r"%smembers of java\.lang\.Class:"%(spaces_pattern), + r"%sname = %s,"%(spaces_pattern, address_pattern), + "}"]) + + checker.check(exec_string, skip_fails=True) + + # print the hub name field and check it is String[] + # n.b. the expected String text is not necessarily null terminated + # so we need a wild card before the final quote + exec_string = execute("x/s (('java.lang.String[]' *)$rdi)->hub->name->value->data") + checker = Checker("print String[] hub name", + r"%s:%s\"\[Ljava.lang.String;%s\""%(address_pattern, spaces_pattern, wildcard_pattern)) + checker.check(exec_string, skip_fails=False) + + # ensure we can reference static fields + exec_string = execute("print 'java.math.BigDecimal'::BIG_TEN_POWERS_TABLE") + checker = Checker("print static field value", + r"%s = \(java.math.BigInteger\[\] \*\) %s"%(wildcard_pattern, address_pattern)) + checker.check(exec_string, skip_fails=False) + + # ensure we can dereference static fields + exec_string = execute("print 'java.math.BigDecimal'::BIG_TEN_POWERS_TABLE->data[3]->mag->data[0]") + checker = Checker("print static field value contents", + r"%s = 1000\$"%(wildcard_pattern)) + checker.check(exec_string, skip_fails=False) # look up PrintStream.println methods # 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\\);", + # "[ \t]*void java.io.PrintStream::println\\(java\\.lang\\.Object \\*\\);", + # "[ \t]*void java.io.PrintStream::println\\(java\\.lang\\.String \\*\\);", # ]) checker = Checker("info func java.io.PrintStream::println", - r"%svoid _java.io.PrintStream::println\(java\.lang\.String\)"%maybe_spaces_pattern) + r"%svoid java.io.PrintStream::println\(java\.lang\.String \*\)"%maybe_spaces_pattern) checker.check(exec_string) # set a break point at PrintStream.println(String) # expect "Breakpoint 2 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)") + exec_string = execute("break java.io.PrintStream::println(java.lang.String *)") rexp = r"Breakpoint 2 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) @@ -234,41 +265,42 @@ def test(): # print details of greeter types exec_string = execute("ptype 'hello.Hello$NamedGreeter'") - rexp = [r"type = class _hello\.Hello\$NamedGreeter : public _hello\.Hello\$Greeter {", + rexp = [r"type = class hello\.Hello\$NamedGreeter : public hello\.Hello\$Greeter {", r"%sprivate:"%(spaces_pattern), - r"%sjava\.lang\.String name;"%(spaces_pattern), + r"%sjava\.lang\.String \*name;"%(spaces_pattern), r"", r"%spublic:"%(spaces_pattern), r"%svoid greet\(void\);"%(spaces_pattern), - r"} \*"] + r"}"] checker = Checker('ptype NamedGreeter', rexp) checker.check(exec_string, skip_fails=False) exec_string = execute("ptype 'hello.Hello$Greeter'") - rexp = [r"type = class _hello\.Hello\$Greeter : public _java\.lang\.Object {", + rexp = [r"type = class hello\.Hello\$Greeter : public java\.lang\.Object {", r"%spublic:"%(spaces_pattern), - r"%sstatic hello\.Hello\$Greeter greeter\(java\.lang\.String\[\]\);"%(spaces_pattern), - r"} \*"] - + r"%sstatic hello\.Hello\$Greeter \* greeter\(java\.lang\.String\[\] \*\);"%(spaces_pattern), + r"}"] + checker = Checker('ptype Greeter', rexp) checker.check(exec_string, skip_fails=False) exec_string = execute("ptype 'java.lang.Object'") - rexp = [r"type = class _java\.lang\.Object : public _objhdr {", + rexp = [r"type = class java\.lang\.Object : public _objhdr {", r"%spublic:"%(spaces_pattern), r"%svoid Object\(void\);"%(spaces_pattern), - r"%sboolean equals\(java\.lang\.Object\);"%(spaces_pattern), + r"%sboolean equals\(java\.lang\.Object \*\);"%(spaces_pattern), r"%sprivate:"%(spaces_pattern), r"%sint hashCode\(void\);"%(spaces_pattern), - r"%sjava\.lang\.String toString\(void\);"%(spaces_pattern), - r"} \*"] + r"%sjava\.lang\.String \* toString\(void\);"%(spaces_pattern), + r"}"] checker = Checker('ptype Object', rexp) checker.check(exec_string, skip_fails=True) exec_string = execute("ptype _objhdr") rexp = [r"type = struct _objhdr {", - r"%sjava\.lang\.Class hub;"%(spaces_pattern), + r"%sjava\.lang\.Class \*hub;"%(spaces_pattern), + r"%sint idHash;"%(spaces_pattern), r"}"] checker = Checker('ptype _objhdr', rexp) checker.check(exec_string, skip_fails=True) @@ -278,9 +310,9 @@ def test(): exec_string = execute("ptype _arrhdrA") rexp = [r"type = struct _arrhdrA {", - r"%sjava\.lang\.Class hub;"%(spaces_pattern), - r"%sint len;"%(spaces_pattern), + r"%sjava\.lang\.Class \*hub;"%(spaces_pattern), r"%sint idHash;"%(spaces_pattern), + r"%sint len;"%(spaces_pattern), r"}"] checker = Checker('ptype _objhdr', rexp) checker.check(exec_string, skip_fails=True) @@ -291,9 +323,9 @@ def test(): # 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%s_hello\.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"#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) ]) checker.check(exec_string, skip_fails=False) @@ -303,7 +335,7 @@ def test(): # check we are still in hello.Hello$Greeter.greeter but no longer in hello.Hello.java exec_string = execute("backtrace 1") checker = Checker("backtrace inline", - [r"#0%s_hello\.Hello\$Greeter::greeter\(java\.lang\.String\[\]\)%s at (%s):%s"%(spaces_pattern, wildcard_pattern, package_file_pattern, digits_pattern)]) + [r"#0%shello\.Hello\$Greeter::greeter\(java\.lang\.String\[\] \*\)%s at (%s):%s"%(spaces_pattern, wildcard_pattern, package_file_pattern, digits_pattern)]) matches = checker.check(exec_string, skip_fails=False) # n.b. can only get back here with one match match = matches[0] @@ -317,10 +349,10 @@ def test(): 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]+" + # 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%s_java\.io\.PrintStream::println\(java\.lang\.String\)%s at %sjava/io/PrintStream.java:%s"%(spaces_pattern, wildcard_pattern, wildcard_pattern, digits_pattern)]) + [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) # list current line @@ -342,37 +374,39 @@ def test(): checker = Checker('list println 2', rexp) checker.check(exec_string, skip_fails=False) - # print the java.io.PrintStream instance and check its type - exec_string = execute("print /x *(('java.io.PrintStream')$rdi)") - checker = Checker("print DefaultGreeterSystem.out", - [r"%s = {"%(wildcard_pattern), - r"%s<_java.io.FilterOutputStream> = {"%(spaces_pattern), - r"%s<_java.io.OutputStream> = {"%(spaces_pattern), - r"%s<_java.lang.Object> = {"%(spaces_pattern), - r"%s<_objhdr> = {"%(spaces_pattern), - r"%shub = %s"%(spaces_pattern, address_pattern), - r"%s}, }, },"%(spaces_pattern), - r"%smembers of _java.io.FilterOutputStream:"%(spaces_pattern), - r"%sout = %s,"%(spaces_pattern, address_pattern), - r"%scloseLock = %s,"%(spaces_pattern, address_pattern), - r"%sclosed = 0x0"%(spaces_pattern), - r"%s},"%(spaces_pattern), - r"%smembers of _java.io.PrintStream:"%(spaces_pattern), - r"%sautoFlush = 0x1,"%(spaces_pattern), - r"%sclosing = 0x0,"%(spaces_pattern), - r"%stextOut = %s,"%(spaces_pattern, address_pattern), - r"%scharOut = %s"%(spaces_pattern, address_pattern), - r"}"]) - - checker.check(exec_string, skip_fails=True) - - # print the hub name field and check it is java.io.PrintStream - # n.b. the expected String text is not necessarily null terminated - # so we need a wild card before the final quote - exec_string = execute("x/s (('java.io.PrintStream')$rdi)->hub->name->value->data") - checker = Checker("print PrintStream hub name", - r"%s:%s\"java.io.PrintStream.*\""%(address_pattern, spaces_pattern)) - checker.check(exec_string, skip_fails=False) + if can_print_data: + # print the java.io.PrintStream instance and check its type + exec_string = execute("print /x *(('java.io.PrintStream' *)$rdi)") + checker = Checker("print DefaultGreeterSystem.out", + [r"%s = {"%(wildcard_pattern), + r"%s = {"%(spaces_pattern), + r"%s = {"%(spaces_pattern), + r"%s = {"%(spaces_pattern), + r"%s<_objhdr> = {"%(spaces_pattern), + r"%shub = %s,"%(spaces_pattern, address_pattern), + r"%sidHash = %s"%(spaces_pattern, address_pattern), + r"%s}, }, },"%(spaces_pattern), + r"%smembers of java.io.FilterOutputStream:"%(spaces_pattern), + r"%sclosed = 0x0,"%(spaces_pattern), + r"%sout = %s,"%(spaces_pattern, address_pattern), + r"%scloseLock = %s"%(spaces_pattern, address_pattern), + r"%s},"%(spaces_pattern), + r"%smembers of java.io.PrintStream:"%(spaces_pattern), + r"%stextOut = %s,"%(spaces_pattern, address_pattern), + r"%scharOut = %s,"%(spaces_pattern, address_pattern), + r"%sautoFlush = 0x1,"%(spaces_pattern), + r"%sclosing = 0x0"%(spaces_pattern), + r"}"]) + + checker.check(exec_string, skip_fails=True) + + # print the hub name field and check it is java.io.PrintStream + # n.b. the expected String text is not necessarily null terminated + # so we need a wild card before the final quote + exec_string = execute("x/s (('java.io.PrintStream' *)$rdi)->hub->name->value->data") + checker = Checker("print PrintStream hub name", + r"%s:%s\"java.io.PrintStream.*\""%(address_pattern, spaces_pattern)) + checker.check(exec_string, skip_fails=False) print(execute("quit 0")) diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ArrayTypeEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ArrayTypeEntry.java index a4d0fa4292a9..080464d2ca49 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ArrayTypeEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ArrayTypeEntry.java @@ -49,7 +49,7 @@ public DebugTypeKind typeKind() { public void addDebugInfo(DebugInfoBase debugInfoBase, DebugTypeInfo debugTypeInfo, DebugContext debugContext) { DebugArrayTypeInfo debugArrayTypeInfo = (DebugArrayTypeInfo) debugTypeInfo; String elementTypeName = TypeEntry.canonicalize(debugArrayTypeInfo.elementType()); - elementType = debugInfoBase.lookupTypeEntry(elementTypeName); + this.elementType = debugInfoBase.lookupTypeEntry(elementTypeName); this.headerSize = debugArrayTypeInfo.headerSize(); this.lengthOffset = debugArrayTypeInfo.lengthOffset(); debugContext.log("typename %s element type %s header size %d length offset %d\n", typeName, elementTypeName, headerSize, lengthOffset); 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 4d9d528fe270..48abf55bf4ba 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 @@ -111,6 +111,31 @@ public ClassEntry(String className, FileEntry fileEntry, int size) { } } + @Override + public DebugTypeKind typeKind() { + return DebugTypeKind.INSTANCE; + } + + @Override + public void addDebugInfo(DebugInfoBase debugInfoBase, DebugTypeInfo debugTypeInfo, DebugContext debugContext) { + assert TypeEntry.canonicalize(debugTypeInfo.typeName()).equals(typeName); + DebugInstanceTypeInfo debugInstanceTypeInfo = (DebugInstanceTypeInfo) debugTypeInfo; + /* Add details of super and interface classes */ + String superName = debugInstanceTypeInfo.superName(); + if (superName != null) { + superName = TypeEntry.canonicalize(superName); + } + debugContext.log("typename %s adding super %s\n", typeName, superName); + if (superName != null) { + this.superClass = debugInfoBase.lookupClassEntry(superName); + } + debugInstanceTypeInfo.interfaces().forEach(interfaceName -> processInterface(interfaceName, debugInfoBase, debugContext)); + /* 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.processMethod(methodFieldInfo, debugInfoBase, debugContext)); + } + public void indexPrimary(Range primary, List frameSizeInfos, int frameSize) { if (primaryIndex.get(primary) == null) { PrimaryEntry primaryEntry = new PrimaryEntry(primary, frameSizeInfos, frameSize, this); @@ -122,36 +147,32 @@ public void indexPrimary(Range primary, List frameSizeInfo /* deopt targets should all come after normal methods */ assert includesDeoptTarget == false; } - FileEntry fileEntry = primary.getFileEntry(); - assert fileEntry != null; - indexFileEntry(fileEntry); + FileEntry primaryFileEntry = primary.getFileEntry(); + assert primaryFileEntry != null; + indexLocalFileEntry(primaryFileEntry); } } public void indexSubRange(Range subrange) { Range primary = subrange.getPrimary(); - /* - * the subrange should belong to a primary range - */ + /* The subrange should belong to a primary range. */ assert primary != null; PrimaryEntry primaryEntry = primaryIndex.get(primary); - /* - * we should already have seen the primary range - */ + /* 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) { - indexFileEntry(subFileEntry); + indexLocalFileEntry(subFileEntry); } } - private void indexFileEntry(FileEntry fileEntry) { - if (localFilesIndex.get(fileEntry) == null) { - localFiles.add(fileEntry); - localFilesIndex.put(fileEntry, localFiles.size()); - DirEntry dirEntry = fileEntry.getDirEntry(); + private void indexLocalFileEntry(FileEntry localFileEntry) { + if (localFilesIndex.get(localFileEntry) == null) { + localFiles.add(localFileEntry); + localFilesIndex.put(localFileEntry, localFiles.size()); + DirEntry dirEntry = localFileEntry.getDirEntry(); if (dirEntry != null && localDirsIndex.get(dirEntry) == null) { localDirs.add(dirEntry); localDirsIndex.put(dirEntry, localDirs.size()); @@ -205,6 +226,7 @@ public LinkedList getPrimaryEntries() { return primaryEntries; } + @SuppressWarnings("unused") public Object primaryIndexFor(Range primaryRange) { return primaryIndex.get(primaryRange); } @@ -231,31 +253,6 @@ public String getCachePath() { return ""; } - @Override - public DebugTypeKind typeKind() { - return DebugTypeKind.INSTANCE; - } - - @Override - public void addDebugInfo(DebugInfoBase debugInfoBase, DebugTypeInfo debugTypeInfo, DebugContext debugContext) { - assert TypeEntry.canonicalize(debugTypeInfo.typeName()).equals(typeName); - DebugInstanceTypeInfo debugInstanceTypeInfo = (DebugInstanceTypeInfo) debugTypeInfo; - /* Add details of super and interface classes */ - String superName = debugInstanceTypeInfo.superName(); - if (superName != null) { - superName = TypeEntry.canonicalize(superName); - } - debugContext.log("typename %s adding super %s\n", typeName, superName); - if (superName != null) { - this.superClass = debugInfoBase.lookupClassEntry(superName); - } - debugInstanceTypeInfo.interfaces().forEach(interfaceName -> processInterface(interfaceName, debugInfoBase, debugContext)); - /* 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.processMethod(methodFieldInfo, debugInfoBase, debugContext)); - } - private void processInterface(String interfaceName, DebugInfoBase debugInfoBase, DebugContext debugContext) { debugContext.log("typename %s adding interface %s\n", typeName, interfaceName); ClassEntry entry = debugInfoBase.lookupClassEntry(TypeEntry.canonicalize(interfaceName)); @@ -289,11 +286,11 @@ protected void processMethod(DebugMethodInfo debugMethodInfo, DebugInfoBase debu Path cachePath = debugMethodInfo.cachePath(); // n.b. the method file may differ from the owning class file when the method is a // substitution - FileEntry fileEntry = debugInfoBase.ensureFileEntry(fileName, filePath, cachePath); - methods.add(new MethodEntry(fileEntry, methodName, this, resultType, paramTypeArray, paramNameArray, modifiers)); + FileEntry methodFileEntry = debugInfoBase.ensureFileEntry(fileName, filePath, cachePath); + methods.add(new MethodEntry(methodFileEntry, methodName, this, resultType, paramTypeArray, paramNameArray, modifiers)); } - private String formatParams(List paramTypes, List paramNames) { + private static String formatParams(List paramTypes, List paramNames) { if (paramNames.size() == 0) { return ""; } @@ -321,23 +318,27 @@ public ClassEntry getSuperClass() { return superClass; } - public Range makePrimaryRange(String methodName, String symbolName, String paramSignature, String returnTypeName, StringTable stringTable, FileEntry fileEntry, int lo, int hi, int primaryLine, + public Range makePrimaryRange(String methodName, String symbolName, String paramSignature, String returnTypeName, StringTable stringTable, FileEntry primaryFileEntry, int lo, + int hi, int primaryLine, int modifiers, boolean isDeoptTarget) { - if (fileEntry == null) { - // search for a matching method to supply the file entry - // or failing that use the one from this class + FileEntry fileEntryToUse = primaryFileEntry; + if (fileEntryToUse == null) { + /* + * Search for a matching method to supply the file entry or failing that use the one + * from this class. + */ for (MethodEntry methodEntry : methods) { - if (methodEntry.match(methodName, paramSignature, returnTypeName, isDeoptTarget)) { + if (methodEntry.match(methodName, paramSignature, returnTypeName)) { // maybe the method's file entry - fileEntry = methodEntry.getFileEntry(); + fileEntryToUse = methodEntry.getFileEntry(); break; } } - if (fileEntry == null) { - // last chance is the class's file entry - fileEntry = this.fileEntry; + if (fileEntryToUse == null) { + /* last chance is the class's file entry */ + fileEntryToUse = this.fileEntry; } } - return new Range(this.typeName, methodName, symbolName, paramSignature, returnTypeName, stringTable, fileEntry, lo, hi, primaryLine, modifiers, isDeoptTarget); + return new Range(this.typeName, methodName, symbolName, paramSignature, returnTypeName, stringTable, fileEntryToUse, lo, hi, primaryLine, modifiers, isDeoptTarget); } } 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 362111eda522..6c8910e1ccef 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 @@ -113,10 +113,16 @@ public abstract class DebugInfoBase { * otherwise false. */ private boolean useHeapBase; + private int oopShiftBitCount; + private int oopFlagBitsMask; + private int oopReferenceByteCount; public DebugInfoBase(ByteOrder byteOrder) { this.byteOrder = byteOrder; this.useHeapBase = true; + this.oopFlagBitsMask = 0; + this.oopShiftBitCount = 0; + this.oopReferenceByteCount = 0; } /** @@ -139,11 +145,24 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { useHeapBase = debugInfoProvider.useHeapBase(); /* - * Ensure we have a null string in the string section. + * save mask for low order flag bits */ + oopFlagBitsMask = debugInfoProvider.oopFlagBitsMask(); + /* flag bits be bewteen 1 and 32 for us to emit as DW_OP_lit */ + assert oopFlagBitsMask > 0 && oopFlagBitsMask < 32; + /* mask must be contiguous from bit 0 */ + assert ((oopFlagBitsMask + 1) & oopFlagBitsMask) == 0; + + /* Save amount we need to shift references by when loading from an object field. */ + oopShiftBitCount = debugInfoProvider.oopShiftBitCount(); + + /* Save number of bytes in a reference field. */ + oopReferenceByteCount = debugInfoProvider.oopReferenceByteCount(); + + /* Ensure we have a null string in the string section. */ stringTable.uniqueDebugString(""); - // create all the types + /* Create all the types. */ debugInfoProvider.typeInfoProvider().forEach(debugTypeInfo -> debugTypeInfo.debugContext((debugContext) -> { String typeName = TypeEntry.canonicalize(debugTypeInfo.typeName()); typeName = stringTable.uniqueDebugString(typeName); @@ -157,7 +176,7 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { addTypeEntry(typeName, fileName, filePath, cachePath, byteSize, typeKind); })); - // now we can cross reference static and instance field details + /* Now we can cross reference static and instance field details. */ debugInfoProvider.typeInfoProvider().forEach(debugTypeInfo -> debugTypeInfo.debugContext((debugContext) -> { String typeName = TypeEntry.canonicalize(debugTypeInfo.typeName()); DebugTypeKind typeKind = debugTypeInfo.typeKind(); @@ -169,7 +188,7 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { debugInfoProvider.codeInfoProvider().forEach(debugCodeInfo -> debugCodeInfo.debugContext((debugContext) -> { /* - * primary file name and full method name need to be written to the debug_str section + * Primary file name and full method name need to be written to the debug_str section. */ String fileName = debugCodeInfo.fileName(); Path filePath = debugCodeInfo.filePath(); @@ -185,7 +204,7 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { boolean isDeoptTarget = debugCodeInfo.isDeoptTarget(); int modifiers = debugCodeInfo.getModifiers(); - // search for a method defining this primary range + /* Search for a method defining this primary range. */ ClassEntry classEntry = ensureClassEntry(className); FileEntry fileEntry = ensureFileEntry(fileName, filePath, cachePath); Range primaryRange = classEntry.makePrimaryRange(methodName, symbolName, paramSignature, returnTypeName, stringTable, fileEntry, lo, hi, primaryLine, modifiers, isDeoptTarget); @@ -218,7 +237,7 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { String provenance = debugDataInfo.getProvenance(); String typeName = debugDataInfo.getTypeName(); String partitionName = debugDataInfo.getPartition(); - // address is heap-register relative pointer + /* Address is heap-register relative pointer. */ long address = debugDataInfo.getAddress(); long size = debugDataInfo.getSize(); debugContext.log(DebugContext.INFO_LEVEL, "Data: address 0x%x size 0x%x type %s partition %s provenance %s ", address, size, typeName, partitionName, provenance); @@ -293,9 +312,7 @@ ClassEntry lookupClassEntry(String typeName) { } private ClassEntry ensureClassEntry(String className) { - /* - * See if we already have an entry. - */ + /* See if we already have an entry. */ ClassEntry classEntry = primaryClassesIndex.get(className); if (classEntry == null) { TypeEntry typeEntry = typesIndex.get(className); @@ -319,14 +336,12 @@ private FileEntry addFileEntry(String fileName, Path filePath, Path cachePath) { FileEntry fileEntry = filesIndex.get(fileAsPath); if (fileEntry == null) { DirEntry dirEntry = ensureDirEntry(filePath); - // ensure file and cachepath are added to the debug_str section + /* Ensure file and cachepath are added to the debug_str section. */ uniqueDebugString(fileName); uniqueDebugString(cachePath.toString()); fileEntry = new FileEntry(fileName, dirEntry, cachePath); files.add(fileEntry); - /* - * Index the file entry by file path. - */ + /* Index the file entry by file path. */ filesIndex.put(fileAsPath, fileEntry); } else { assert (filePath == null || @@ -345,7 +360,7 @@ protected FileEntry ensureFileEntry(String fileName, Path filePath, Path cachePa } else { fileAsPath = filePath.resolve(fileName); } - // Reuse any existing entry + /* Reuse any existing entry. */ FileEntry fileEntry = findFile(fileAsPath); if (fileEntry == null) { fileEntry = addFileEntry(fileName, filePath, cachePath); @@ -359,7 +374,7 @@ private DirEntry ensureDirEntry(Path filePath) { } DirEntry dirEntry = dirsIndex.get(filePath); if (dirEntry == null) { - // ensure dir path is entered into the debug_str section + /* Ensure dir path is entered into the debug_str section. */ uniqueDebugString(filePath.toString()); dirEntry = new DirEntry(filePath); dirsIndex.put(filePath, dirEntry); @@ -417,4 +432,16 @@ public int debugStringIndex(String string) { public boolean useHeapBase() { return useHeapBase; } + + public byte oopFlagBitsMask() { + return (byte) oopFlagBitsMask; + } + + public int oopShiftBitCount() { + return oopShiftBitCount; + } + + public int oopReferenceByteCount() { + return oopReferenceByteCount; + } } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/FileEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/FileEntry.java index b55b0544c939..4e782681ba40 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/FileEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/FileEntry.java @@ -50,6 +50,7 @@ public String getFileName() { } public String getPathName() { + @SuppressWarnings("hiding") DirEntry dirEntry = getDirEntry(); if (dirEntry == null) { return ""; @@ -59,7 +60,6 @@ public String getPathName() { } public String getFullName() { - DirEntry dirEntry = getDirEntry(); if (dirEntry == null) { return fileName; } else { 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 5712b6ec2714..7b84995d6cd5 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 @@ -68,11 +68,11 @@ public String getParamTypeName(int idx) { public String getParamName(int idx) { assert paramNames != null; assert idx < paramNames.length; - // n.b. param names may be null + /* N.b. param names may be null. */ return paramNames[idx]; } - public boolean match(String methodName, String paramSignature, String returnTypeName, boolean isDeoptTarget) { + public boolean match(String methodName, String paramSignature, String returnTypeName) { if (!methodName.equals(this.memberName)) { return 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 5cd1d2d4f9c7..53aaa5a2b7d6 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,6 @@ import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFrameSizeChange; -import java.util.HashMap; import java.util.LinkedList; import java.util.List; @@ -48,10 +47,6 @@ public class PrimaryEntry { * A list of subranges associated with the primary range. */ private List subranges; - /** - * A mapping from subranges to their associated file entry. - */ - private HashMap subrangeIndex; /** * Details of of compiled method frame size changes. */ 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 6cdd3ed052aa..9834420f2915 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java @@ -40,10 +40,8 @@ public class Range { private String symbolName; private String paramSignature; private String returnTypeName; - private String methodNameWithParams; private String fullMethodName; private String fullMethodNameWithParams; - private String fullMethodNameWithParamsAndReturnType; private int lo; private int hi; private int line; @@ -87,7 +85,6 @@ private Range(String className, String methodName, String symbolName, String par this.returnTypeName = stringTable.uniqueString(returnTypeName); this.fullMethodName = stringTable.uniqueString(constructClassAndMethodName()); this.fullMethodNameWithParams = stringTable.uniqueString(constructClassAndMethodNameWithParams()); - this.fullMethodNameWithParamsAndReturnType = stringTable.uniqueString(constructClassAndMethodNameWithParamsAndReturnType()); this.lo = lo; this.hi = hi; this.line = line; @@ -140,10 +137,6 @@ public String getFullMethodNameWithParams() { return fullMethodNameWithParams; } - public String getFullMethodNameWithParamsAndReturnType() { - return fullMethodNameWithParamsAndReturnType; - } - public boolean isDeoptTarget() { return isDeoptTarget; } @@ -179,10 +172,6 @@ private String constructClassAndMethodNameWithParams() { return getExtendedMethodName(true, true, false); } - private String constructClassAndMethodNameWithParamsAndReturnType() { - return getExtendedMethodName(true, true, true); - } - public String getMethodReturnTypeName() { return returnTypeName; } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/StructureTypeEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/StructureTypeEntry.java index 057fcabb4c1d..2e69411c5f72 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/StructureTypeEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/StructureTypeEntry.java @@ -57,11 +57,11 @@ public Stream fields() { protected void processField(DebugFieldInfo debugFieldInfo, DebugInfoBase debugInfoBase, DebugContext debugContext) { String fieldName = debugInfoBase.uniqueDebugString(debugFieldInfo.name()); String valueTypeName = TypeEntry.canonicalize(debugFieldInfo.valueType()); - int size = debugFieldInfo.size(); - int offset = debugFieldInfo.offset(); - int modifiers = debugFieldInfo.modifiers(); + int fieldSize = debugFieldInfo.size(); + int fieldoffset = debugFieldInfo.offset(); + int fieldModifiers = debugFieldInfo.modifiers(); debugContext.log("typename %s adding %s field %s type %s size %s at offset %d\n", - typeName, memberModifiers(modifiers), fieldName, valueTypeName, size, offset); + typeName, memberModifiers(fieldModifiers), fieldName, valueTypeName, fieldSize, fieldoffset); TypeEntry valueType = debugInfoBase.lookupTypeEntry(valueTypeName); String fileName = debugFieldInfo.fileName(); Path filePath = debugFieldInfo.filePath(); @@ -69,7 +69,7 @@ protected void processField(DebugFieldInfo debugFieldInfo, DebugInfoBase debugIn // n.b. the field file may differ from the owning class file when the field is a // substitution FileEntry fileEntry = debugInfoBase.ensureFileEntry(fileName, filePath, cachePath); - fields.add(new FieldEntry(fileEntry, fieldName, this, valueType, size, offset, modifiers)); + fields.add(new FieldEntry(fileEntry, fieldName, this, valueType, fieldSize, fieldoffset, fieldModifiers)); } String memberModifiers(int modifiers) { diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/TypeEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/TypeEntry.java index e74032fe450a..afbbf01e117d 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/TypeEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/TypeEntry.java @@ -98,8 +98,6 @@ public boolean isStructure() { public abstract void addDebugInfo(DebugInfoBase debugInfoBase, DebugTypeInfo debugTypeInfo, DebugContext debugContext); public static String canonicalize(String typeName) { - // typeName = typeName.replace('$', '.'); - typeName = typeName.replace(" ", "__"); - return typeName; + return typeName.replace(" ", "__"); } } 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 3c95d7697d12..4e24ca6f2aa3 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 @@ -40,6 +40,12 @@ public interface DebugInfoProvider { boolean useHeapBase(); + int oopShiftBitCount(); + + int oopFlagBitsMask(); + + int oopReferenceByteCount(); + /** * An interface implemented by items that can be located in a file. */ @@ -90,7 +96,7 @@ public String toString() { return "???"; } } - }; + } void debugContext(Consumer action); diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfARangesSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfARangesSectionImpl.java index b7eae8bd1e6b..02fad181804b 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfARangesSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfARangesSectionImpl.java @@ -37,16 +37,13 @@ import java.util.LinkedList; import java.util.Map; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ARANGES_SECTION_NAME; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_FRAME_SECTION_NAME; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_VERSION_2; - /** * Section generator for debug_aranges section. */ public class DwarfARangesSectionImpl extends DwarfSectionImpl { + /* Headers have a fixed size but must align up to 2 * address size. */ private static final int DW_AR_HEADER_SIZE = 12; - private static final int DW_AR_HEADER_PAD_SIZE = 4; // align up to 2 * address size + private static final int DW_AR_HEADER_PAD_SIZE = 4; public DwarfARangesSectionImpl(DwarfDebugInfo dwarfSections) { super(dwarfSections); @@ -54,7 +51,7 @@ public DwarfARangesSectionImpl(DwarfDebugInfo dwarfSections) { @Override public String getSectionName() { - return DW_ARANGES_SECTION_NAME; + return DwarfDebugInfo.DW_ARANGES_SECTION_NAME; } @Override @@ -187,7 +184,7 @@ public void writeContent(DebugContext context) { log(context, " [0x%08x] %s CU %d length 0x%x", pos, classEntry.getFileName(), cuIndex, length); pos = putInt(length, buffer, pos); /* DWARF version is always 2. */ - pos = putShort(DW_VERSION_2, buffer, pos); + pos = putShort(DwarfDebugInfo.DW_VERSION_2, buffer, pos); pos = putInt(cuIndex, buffer, pos); /* Address size is always 8. */ pos = putByte((byte) 8, buffer, pos); @@ -240,7 +237,7 @@ public void writeContent(DebugContext context) { log(context, " [0x%08x] %s CU linkage stubs %d length 0x%x", pos, classEntry.getFileName(), cuIndex, length); pos = putInt(length, buffer, pos); /* DWARF version is always 2. */ - pos = putShort(DW_VERSION_2, buffer, pos); + pos = putShort(DwarfDebugInfo.DW_VERSION_2, buffer, pos); pos = putInt(cuIndex, buffer, pos); /* Address size is always 8. */ pos = putByte((byte) 8, buffer, pos); @@ -275,7 +272,7 @@ public void writeContent(DebugContext context) { /* * The debug_aranges section depends on debug_frame section. */ - private static final String TARGET_SECTION_NAME = DW_FRAME_SECTION_NAME; + private static final String TARGET_SECTION_NAME = DwarfDebugInfo.DW_FRAME_SECTION_NAME; @Override public String targetSectionName() { 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 243450a16108..c323e18c42e8 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 @@ -29,86 +29,6 @@ import com.oracle.objectfile.LayoutDecision; import org.graalvm.compiler.debug.DebugContext; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_array_data_type; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_array_layout; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_array_pointer; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_array_typedef; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_array_unit; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_builtin_unit; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_class_layout; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_class_pointer; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_class_typedef; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_class_unit1; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_class_unit2; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_field_declaration1; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_field_declaration2; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_field_declaration3; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_field_declaration4; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_header_field; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_interface_implementor; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_interface_layout; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_interface_pointer; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_interface_typedef; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_method_declaration1; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_method_declaration2; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_method_location; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_method_parameter_declaration1; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_method_parameter_declaration2; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_method_parameter_declaration3; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_object_header; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_primitive_type; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_static_field_location; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_super_reference; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_void_type; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_SECTION_NAME; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ARANGES_SECTION_NAME; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_accessibility; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_artificial; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_bit_size; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_byte_size; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_comp_dir; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_containing_type; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_data_member_location; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_decl_file; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_declaration; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_encoding; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_external; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_hi_pc; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_language; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_location; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_low_pc; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_name; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_null; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_specification; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_stmt_list; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_type; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_CHILDREN_no; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_CHILDREN_yes; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_FORM_addr; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_FORM_data1; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_FORM_data2; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_FORM_data4; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_FORM_expr_loc; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_FORM_flag; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_FORM_null; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_FORM_ref_addr; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_FORM_strp; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_TAG_array_type; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_TAG_base_type; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_TAG_class_type; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_TAG_compile_unit; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_TAG_formal_parameter; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_TAG_inheritance; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_TAG_member; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_TAG_pointer_type; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_TAG_structure_type; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_TAG_subprogram; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_TAG_typedef; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_TAG_union_type; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_TAG_unspecified_type; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_TAG_variable; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.Dw_AT_object_pointer; - /** * Section generator for debug_abbrev section. */ @@ -120,7 +40,7 @@ public DwarfAbbrevSectionImpl(DwarfDebugInfo dwarfSections) { @Override public String getSectionName() { - return DW_ABBREV_SECTION_NAME; + return DwarfDebugInfo.DW_ABBREV_SECTION_NAME; } @Override @@ -201,9 +121,6 @@ public void createContent() { *
        • code = class_pointer, tag = pointer_type, parent = class_unit - Java * instance ref type * - *
        • code = class_typedef, tag = typedef, parent = class_unit - Java instance - * ref typedef - * *
        • code = method_location, tag = subprogram , parent = class_unit - Java * method code definition (i.e. location of code) * @@ -216,18 +133,12 @@ public void createContent() { *
        • code = array_pointer, tag = pointer_type, parent = array_unit - Java * array ref type * - *
        • code = array_typedef, tag = typedef, parent = array_unit - Java array ref - * typedef - * *
        • code = interface_layout, tag = union_type, parent = class_unit - Java * array type structure definition * *
        • code = interface_pointer, tag = pointer_type, parent = class_unit - Java * interface ref type * - *
        • code = interface_typedef, tag = typedef, parent = class_unit - Java array - * ref typedef - * *
        * *
          @@ -279,7 +190,7 @@ public void createContent() { *
            * * The void type is defined as an unspecified type - * + * *
          • abbrev_code == void_type, tag == DW_TAG_unspecified_type, no_children * *
          • DW_AT_name : ........ DW_FORM_strp @@ -302,6 +213,8 @@ public void createContent() { * *
          • DW_AT_byte_size : ... DW_FORM_data1 "oop" * + *
          • DW_AT_location : ........ DW_FORM_expr_loc + * *
          * * Header Data: A level 1 DIE of type member is used to describe the fields of both object @@ -365,7 +278,7 @@ public void createContent() { *
        • Dw_AT_decl_line : ... DW_FORM_data1/2 ??? how many bytes do we really * need? * - *
        • Dw_AT_containing_type : ..... DW_FORM_ref_addr + *
        • Dw_AT_data_location : ... DW_FORM_expr_loc * *
        * @@ -477,20 +390,11 @@ public void createContent() { *
      • Dw_AT_artificial : ... DW_FORM_flag (optional only for * method_parameter_declaration1 $this, $access) * - *
      • Dw_AT_location(list???) : ... DW_FORM_exprloc - * *
      * * Instance Class Reference Types: A level 1 class_layout DIE is followed by a DIE defining - * a pointer to the class and a second DIE that defines a typedef for that pointer using the - * Java class name as the typedef name. This reflects the fact that a Java object reference - * is actually implemented as a pointer. - * - * n.b. the name used in the class_layout DIE is not the Java class name. It is derived by - * appending '_' to the Java class name (preceding the package prefix). So this means that, - * for example, the Java type java.lang.Object appears to gdb to be defined as follows - * - * typedef struct _java.lang.Object { ... } *java.lang.Object; + * a pointer to the class. This reflects the fact that a Java object reference is actually + * implemented as a pointer. * *
        * @@ -502,13 +406,28 @@ public void createContent() { * *
      * + * n.b. the name used in the class_layout DIE is the Java class name. This is deliberately + * inconsistent with the Java naming where the name refers to the pointer type. In + * consequence when gdb displays Java types and signatures oop reference appear as pointer + * types. So, for example the Java String class looks like + * *
        * - *
      • abbrev_code == class_typedef, tag == DW_TAG_typedef, no_children + *
      • class java.lang.String : public java.lang.Object { * - *
      • Dw_AT_name : ... DW_FORM_strp + *
      • private: * - *
      • Dw_AT_type : ........ DW_FORM_ref_addr + *
      • byte[] value; + * + *
      • ... + * + *
      • public: + * + *
      • ... + * + *
      • java.lang.String *concat(java.lang.String *); + * + *
      • ... * *
      * @@ -547,7 +466,7 @@ public void createContent() { * *
    • DW_AT_linkage_name : .... DW_FORM_strp * - *
    • DW_AT_location : ........ DW_FORM_exprloc + *
    • DW_AT_location : ........ DW_FORM_expr_loc * *
    * @@ -563,7 +482,7 @@ public void createContent() { * * * - * Array Structure: Each array_unit DIE contains three level 1 DIEs. The first one describes + * Array Structure: Each array_unit DIE contains two level 1 DIEs. The first one describes * the array layout: * *
      @@ -575,6 +494,8 @@ public void createContent() { *
    • Dw_AT_byte_size : ... DW_FORM_data1/2 size up to base of embedded array * elements? * + *
    • DW_AT_location : .... DW_FORM_expr_loc + * *
    * * The second DIE defines the array reference type as a pointer to the underlying structure @@ -588,23 +509,10 @@ public void createContent() { * *
  • Dw_AT_type : ........ DW_FORM_ref_addr * - * The third DIE defines the array type name as a typedef for the pointer type - * - *
      - * - *
    • abbrev_code == array_typedef, tag == DW_TAG_typedef, no_children - * - *
    • Dw_AT_name : ....... DW_FORM_strp - * - *
    • Dw_AT_type : ........ DW_FORM_ref_addr - * - *
    - * - * n.b. the name used in the array_layout DIE is not the Java array name. It is derived by - * appending '_' to the Java array name (preceding the package prefix). So this means that, - * for example, the Java type java.lang.String[] appears to gdb to be defined as follows - * - * typedef struct _java.lang.String[] { ... } *java.lang.String[]; + * n.b. the name used in the array_layout DIE is the Java array name. This is deliberately + * inconsistent with the Java naming where the name refers to the pointer type. As with + * normal objects an array reference in a Java signature appears as a pointer to an array + * layout when printed by gdb. * * Array members: The level 1 array_layout DIE includes level 2 child DIEs with tag member * that describe the layout of the array. header_field DIEs are used to declare members of @@ -627,16 +535,13 @@ public void createContent() { * * Interface Layout and Reference Types: The level 0 class_unit DIE for an interface is * followed by a level 1 DIE defining the interface layout as a union of all the layouts for - * the classes which implement the interface. Two more level 1 DIEs define the a pointer to - * this layout type and a typedef that names the interface pointer type using the Java - * interface name. - * - * n.b. the name used in the interface_layout DIE is not the Java interface name. It is - * derived by appending '_' to the Java class name (preceding the package prefix). So this - * means that, for example, the Java interface java.lang.CharSequence appears to gdb to be - * defined as follows + * the classes which implement the interface. A second level 1 DIEs defines a pointer to + * this layout type. * - * typedef union _java.lang.CharSequence { ... } *java.lang.CharSequence; + * n.b. the name used in the interface_layout DIE is the Java array name. This is + * deliberately inconsistent with the Java naming where the name refers to the pointer type. + * As with normal objects an interface reference in a Java signature appears as a pointer to + * an interface layout when printed by gdb. * *
      * @@ -644,6 +549,8 @@ public void createContent() { * *
    • Dw_AT_name : ....... DW_FORM_strp * + *
    • DW_AT_location : ... DW_FORM_expr_loc + * *
    * *
      @@ -675,8 +582,8 @@ public void createContent() { * class. So, this means that, for example, the Java interface java.lang.CharSequence will * include members for String, StringBuffer etc as follows * - * typedef union _java.lang.CharSequence { _java.lang.String _java.lang.String; - * _java.lang.StringBuffer _java.lang.StringBuffer; ... } *java.lang.CharSequence; + * union java.lang.CharSequence { java.lang.String _java.lang.String; + * java.lang.StringBuffer _java.lang.StringBuffer; ... }; * */ @@ -702,7 +609,8 @@ public void writeContent(DebugContext context) { assert pos == size; } - public int writeAbbrevs(DebugContext context, byte[] buffer, int pos) { + public int writeAbbrevs(DebugContext context, byte[] buffer, int p) { + int pos = p; pos = writeBuiltInUnitAbbrev(context, buffer, pos); pos = writeClassUnitAbbrevs(context, buffer, pos); pos = writeArrayUnitAbbrev(context, buffer, pos); @@ -747,555 +655,526 @@ private int writeAttrForm(long code, byte[] buffer, int pos) { } } - private int writeBuiltInUnitAbbrev(DebugContext context, byte[] buffer, int p) { + private int writeBuiltInUnitAbbrev(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { int pos = p; - pos = writeAbbrevCode(DW_ABBREV_CODE_builtin_unit, buffer, pos); - pos = writeTag(DW_TAG_compile_unit, buffer, pos); - pos = writeFlag(DW_CHILDREN_yes, buffer, pos); - pos = writeAttrType(DW_AT_language, buffer, pos); - pos = writeAttrForm(DW_FORM_data1, buffer, pos); + pos = writeAbbrevCode(DwarfDebugInfo.DW_ABBREV_CODE_builtin_unit, buffer, pos); + pos = writeTag(DwarfDebugInfo.DW_TAG_compile_unit, buffer, pos); + pos = writeFlag(DwarfDebugInfo.DW_CHILDREN_yes, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_language, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data1, buffer, pos); /* * Now terminate. */ - pos = writeAttrType(DW_AT_null, buffer, pos); - pos = writeAttrForm(DW_FORM_null, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_null, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_null, buffer, pos); return pos; } private int writeClassUnitAbbrevs(DebugContext context, byte[] buffer, int p) { int pos = p; - // class compile unit no line info - pos = writeClassUnitAbbrev(context, DW_ABBREV_CODE_class_unit1, buffer, pos); - // class compile unit with line info - pos = writeClassUnitAbbrev(context, DW_ABBREV_CODE_class_unit2, buffer, pos); + /* class compile unit no line info */ + pos = writeClassUnitAbbrev(context, DwarfDebugInfo.DW_ABBREV_CODE_class_unit1, buffer, pos); + /* class compile unit with line info */ + pos = writeClassUnitAbbrev(context, DwarfDebugInfo.DW_ABBREV_CODE_class_unit2, buffer, pos); return pos; } - private int writeClassUnitAbbrev(DebugContext context, int abbrevCode, byte[] buffer, int p) { + private int writeClassUnitAbbrev(@SuppressWarnings("unused") DebugContext context, int abbrevCode, byte[] buffer, int p) { int pos = p; pos = writeAbbrevCode(abbrevCode, buffer, pos); - pos = writeTag(DW_TAG_compile_unit, buffer, pos); - pos = writeFlag(DW_CHILDREN_yes, buffer, pos); - pos = writeAttrType(DW_AT_language, buffer, pos); - pos = writeAttrForm(DW_FORM_data1, buffer, pos); - pos = writeAttrType(DW_AT_name, buffer, pos); - pos = writeAttrForm(DW_FORM_strp, buffer, pos); - pos = writeAttrType(DW_AT_comp_dir, buffer, pos); - pos = writeAttrForm(DW_FORM_strp, buffer, pos); - pos = writeAttrType(DW_AT_low_pc, buffer, pos); - pos = writeAttrForm(DW_FORM_addr, buffer, pos); - pos = writeAttrType(DW_AT_hi_pc, buffer, pos); - pos = writeAttrForm(DW_FORM_addr, buffer, pos); - if (abbrevCode == DW_ABBREV_CODE_class_unit1) { - pos = writeAttrType(DW_AT_stmt_list, buffer, pos); - pos = writeAttrForm(DW_FORM_data4, buffer, pos); + pos = writeTag(DwarfDebugInfo.DW_TAG_compile_unit, buffer, pos); + pos = writeFlag(DwarfDebugInfo.DW_CHILDREN_yes, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_language, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data1, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_name, buffer, pos); + 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) { + pos = writeAttrType(DwarfDebugInfo.DW_AT_stmt_list, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data4, buffer, pos); } /* * Now terminate. */ - pos = writeAttrType(DW_AT_null, buffer, pos); - pos = writeAttrForm(DW_FORM_null, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_null, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_null, buffer, pos); return pos; } - private int writeArrayUnitAbbrev(DebugContext context, byte[] buffer, int p) { + private int writeArrayUnitAbbrev(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { int pos = p; - pos = writeAbbrevCode(DW_ABBREV_CODE_array_unit, buffer, pos); - pos = writeTag(DW_TAG_compile_unit, buffer, pos); - pos = writeFlag(DW_CHILDREN_yes, buffer, pos); - pos = writeAttrType(DW_AT_language, buffer, pos); - pos = writeAttrForm(DW_FORM_data1, buffer, pos); - pos = writeAttrType(DW_AT_name, buffer, pos); - pos = writeAttrForm(DW_FORM_strp, buffer, pos); + pos = writeAbbrevCode(DwarfDebugInfo.DW_ABBREV_CODE_array_unit, buffer, pos); + pos = writeTag(DwarfDebugInfo.DW_TAG_compile_unit, buffer, pos); + pos = writeFlag(DwarfDebugInfo.DW_CHILDREN_yes, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_language, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data1, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_name, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_strp, buffer, pos); /* * Now terminate. */ - pos = writeAttrType(DW_AT_null, buffer, pos); - pos = writeAttrForm(DW_FORM_null, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_null, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_null, buffer, pos); return pos; } - private int writePrimitiveTypeAbbrev(DebugContext context, byte[] buffer, int p) { + private int writePrimitiveTypeAbbrev(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { int pos = p; - pos = writeAbbrevCode(DW_ABBREV_CODE_primitive_type, buffer, pos); - pos = writeTag(DW_TAG_base_type, buffer, pos); - pos = writeFlag(DW_CHILDREN_no, buffer, pos); - pos = writeAttrType(DW_AT_byte_size, buffer, pos); - pos = writeAttrForm(DW_FORM_data1, buffer, pos); - pos = writeAttrType(DW_AT_bit_size, buffer, pos); - pos = writeAttrForm(DW_FORM_data1, buffer, pos); - pos = writeAttrType(DW_AT_encoding, buffer, pos); - pos = writeAttrForm(DW_FORM_data1, buffer, pos); - pos = writeAttrType(DW_AT_name, buffer, pos); - pos = writeAttrForm(DW_FORM_strp, buffer, pos); + pos = writeAbbrevCode(DwarfDebugInfo.DW_ABBREV_CODE_primitive_type, buffer, pos); + pos = writeTag(DwarfDebugInfo.DW_TAG_base_type, buffer, pos); + pos = writeFlag(DwarfDebugInfo.DW_CHILDREN_no, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_byte_size, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data1, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_bit_size, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data1, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_encoding, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data1, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_name, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_strp, buffer, pos); /* * Now terminate. */ - pos = writeAttrType(DW_AT_null, buffer, pos); - pos = writeAttrForm(DW_FORM_null, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_null, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_null, buffer, pos); return pos; } - private int writeVoidTypeAbbrev(DebugContext context, byte[] buffer, int p) { + private int writeVoidTypeAbbrev(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { int pos = p; - pos = writeAbbrevCode(DW_ABBREV_CODE_void_type, buffer, pos); - pos = writeTag(DW_TAG_unspecified_type, buffer, pos); - pos = writeFlag(DW_CHILDREN_no, buffer, pos); - pos = writeAttrType(DW_AT_name, buffer, pos); - pos = writeAttrForm(DW_FORM_strp, buffer, pos); + pos = writeAbbrevCode(DwarfDebugInfo.DW_ABBREV_CODE_void_type, buffer, pos); + pos = writeTag(DwarfDebugInfo.DW_TAG_unspecified_type, buffer, pos); + pos = writeFlag(DwarfDebugInfo.DW_CHILDREN_no, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_name, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_strp, buffer, pos); /* * Now terminate. */ - pos = writeAttrType(DW_AT_null, buffer, pos); - pos = writeAttrForm(DW_FORM_null, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_null, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_null, buffer, pos); return pos; } - private int writeObjectHeaderAbbrev(DebugContext context, byte[] buffer, int p) { + private int writeObjectHeaderAbbrev(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { int pos = p; - pos = writeAbbrevCode(DW_ABBREV_CODE_object_header, buffer, pos); - pos = writeTag(DW_TAG_structure_type, buffer, pos); - pos = writeFlag(DW_CHILDREN_yes, buffer, pos); - pos = writeAttrType(DW_AT_name, buffer, pos); - pos = writeAttrForm(DW_FORM_strp, buffer, pos); - pos = writeAttrType(DW_AT_byte_size, buffer, pos); - pos = writeAttrForm(DW_FORM_data1, buffer, pos); + pos = writeAbbrevCode(DwarfDebugInfo.DW_ABBREV_CODE_object_header, buffer, pos); + pos = writeTag(DwarfDebugInfo.DW_TAG_structure_type, buffer, pos); + pos = writeFlag(DwarfDebugInfo.DW_CHILDREN_yes, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_name, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_strp, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_byte_size, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data1, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_data_location, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_expr_loc, buffer, pos); /* * Now terminate. */ - pos = writeAttrType(DW_AT_null, buffer, pos); - pos = writeAttrForm(DW_FORM_null, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_null, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_null, buffer, pos); return pos; } - private int writeClassLayoutAbbrev(DebugContext context, byte[] buffer, int p) { + private int writeClassLayoutAbbrev(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { int pos = p; - pos = writeAbbrevCode(DW_ABBREV_CODE_class_layout, buffer, pos); - pos = writeTag(DW_TAG_class_type, buffer, pos); - pos = writeFlag(DW_CHILDREN_yes, buffer, pos); - pos = writeAttrType(DW_AT_name, buffer, pos); - pos = writeAttrForm(DW_FORM_strp, buffer, pos); - pos = writeAttrType(DW_AT_byte_size, buffer, pos); - pos = writeAttrForm(DW_FORM_data2, buffer, pos); - pos = writeAttrType(DW_AT_decl_file, buffer, pos); - pos = writeAttrForm(DW_FORM_data2, buffer, pos); - // at present we definitely don't have line numbers - // pos = writeAttrType(DW_AT_decl_line, buffer, pos); - // pos = writeAttrForm(DW_FORM_data2, buffer, pos); - // n.b. the containing_type attribute is not strict DWARF but gdb expects it - // we also add an inheritance member with the same info - pos = writeAttrType(DW_AT_containing_type, buffer, pos); - pos = writeAttrForm(DW_FORM_ref_addr, buffer, pos); + pos = writeAbbrevCode(DwarfDebugInfo.DW_ABBREV_CODE_class_layout, buffer, pos); + pos = writeTag(DwarfDebugInfo.DW_TAG_class_type, buffer, pos); + pos = writeFlag(DwarfDebugInfo.DW_CHILDREN_yes, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_name, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_strp, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_byte_size, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data2, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_decl_file, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data2, buffer, pos); + /*- + * At present we definitely don't have line numbers. + pos = writeAttrType(DwarfDebugInfo.DW_AT_decl_line, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data2, buffer, pos); + */ + /* Add a data location expression to mask and/or rebase oop pointers. */ + pos = writeAttrType(DwarfDebugInfo.DW_AT_data_location, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_expr_loc, buffer, pos); /* * Now terminate. */ - pos = writeAttrType(DW_AT_null, buffer, pos); - pos = writeAttrForm(DW_FORM_null, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_null, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_null, buffer, pos); return pos; } - private int writeClassReferenceAbbrev(DebugContext context, byte[] buffer, int p) { + private int writeClassReferenceAbbrev(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { int pos = p; - // first the basic pointer type for a pointer to the class struct type - pos = writeAbbrevCode(DW_ABBREV_CODE_class_pointer, buffer, pos); - pos = writeTag(DW_TAG_pointer_type, buffer, pos); - pos = writeFlag(DW_CHILDREN_no, buffer, pos); - pos = writeAttrType(DW_AT_byte_size, buffer, pos); - pos = writeAttrForm(DW_FORM_data1, buffer, pos); - pos = writeAttrType(DW_AT_type, buffer, pos); - pos = writeAttrForm(DW_FORM_ref_addr, buffer, pos); - /* - * Now terminate. - */ - pos = writeAttrType(DW_AT_null, buffer, pos); - pos = writeAttrForm(DW_FORM_null, buffer, pos); - - // we wll also need a typedef to advertise the class name as the pointer type - pos = writeAbbrevCode(DW_ABBREV_CODE_class_typedef, buffer, pos); - pos = writeTag(DW_TAG_typedef, buffer, pos); - pos = writeFlag(DW_CHILDREN_no, buffer, pos); - pos = writeAttrType(DW_AT_name, buffer, pos); - pos = writeAttrForm(DW_FORM_strp, buffer, pos); - pos = writeAttrType(DW_AT_type, buffer, pos); - pos = writeAttrForm(DW_FORM_ref_addr, buffer, pos); + /* First the basic pointer type for a pointer to the class struct type. */ + pos = writeAbbrevCode(DwarfDebugInfo.DW_ABBREV_CODE_class_pointer, buffer, pos); + pos = writeTag(DwarfDebugInfo.DW_TAG_pointer_type, buffer, pos); + pos = writeFlag(DwarfDebugInfo.DW_CHILDREN_no, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_byte_size, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data1, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_type, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref_addr, buffer, pos); /* * Now terminate. */ - pos = writeAttrType(DW_AT_null, buffer, pos); - pos = writeAttrForm(DW_FORM_null, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_null, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_null, buffer, pos); return pos; } - private int writeMethodDeclarationAbbrevs(DebugContext context, byte[] buffer, int p) { + private int writeMethodDeclarationAbbrevs(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { int pos = p; - pos = writeMethodDeclarationAbbrev(context, DW_ABBREV_CODE_method_declaration1, buffer, pos); - pos = writeMethodDeclarationAbbrev(context, DW_ABBREV_CODE_method_declaration2, buffer, pos); + pos = writeMethodDeclarationAbbrev(context, DwarfDebugInfo.DW_ABBREV_CODE_method_declaration1, buffer, pos); + pos = writeMethodDeclarationAbbrev(context, DwarfDebugInfo.DW_ABBREV_CODE_method_declaration2, buffer, pos); return pos; } - private int writeMethodDeclarationAbbrev(DebugContext context, int abbrevCode, byte[] buffer, int p) { + private int writeMethodDeclarationAbbrev(@SuppressWarnings("unused") DebugContext context, int abbrevCode, byte[] buffer, int p) { int pos = p; pos = writeAbbrevCode(abbrevCode, buffer, pos); - pos = writeTag(DW_TAG_subprogram, buffer, pos); - pos = writeFlag(DW_CHILDREN_yes, buffer, pos); - pos = writeAttrType(DW_AT_external, buffer, pos); - pos = writeAttrForm(DW_FORM_flag, buffer, pos); - pos = writeAttrType(DW_AT_name, buffer, pos); - pos = writeAttrForm(DW_FORM_strp, buffer, pos); - pos = writeAttrType(DW_AT_decl_file, buffer, pos); - pos = writeAttrForm(DW_FORM_data2, buffer, pos); - // pos = writeAttrType(DW_AT_decl_line, buffer, pos); - // pos = writeAttrForm(DW_FORM_data2, buffer, pos); - // pos = writeAttrType(Dw_AT_linkage_name, buffer, pos); // not in DWARF2 - // pos = writeAttrForm(DW_FORM_strp, buffer, pos); - pos = writeAttrType(DW_AT_type, buffer, pos); - pos = writeAttrForm(DW_FORM_ref_addr, buffer, pos); - pos = writeAttrType(DW_AT_artificial, buffer, pos); - pos = writeAttrForm(DW_FORM_flag, buffer, pos); - pos = writeAttrType(DW_AT_accessibility, buffer, pos); - pos = writeAttrForm(DW_FORM_data1, buffer, pos); - pos = writeAttrType(DW_AT_declaration, buffer, pos); - pos = writeAttrForm(DW_FORM_flag, buffer, pos); - // pos = writeAttrType(DW_AT_virtuality, buffer, pos); - // pos = writeAttrForm(DW_FORM_data1, buffer, pos); - pos = writeAttrType(DW_AT_containing_type, buffer, pos); - pos = writeAttrForm(DW_FORM_ref_addr, buffer, pos); - if (abbrevCode == DW_ABBREV_CODE_method_declaration1) { - pos = writeAttrType(Dw_AT_object_pointer, buffer, pos); - pos = writeAttrForm(DW_FORM_ref_addr, buffer, pos); + pos = writeTag(DwarfDebugInfo.DW_TAG_subprogram, buffer, pos); + pos = writeFlag(DwarfDebugInfo.DW_CHILDREN_yes, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_external, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_flag, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_name, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_strp, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_decl_file, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data2, buffer, pos); + /* We don't (yet?) have a proper start line for the method itself */ + // pos = writeAttrType(DwarfDebugInfo.DW_AT_decl_line, buffer, pos); + // pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data2, buffer, pos); + /* This probably needs to use the symbol name -- n.b. it is not in DWARF2 */ + // pos = writeAttrType(DwarfDebugInfo.DW_AT_linkage_name, buffer, pos); + // pos = writeAttrForm(DwarfDebugInfo.DW_FORM_strp, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_type, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref_addr, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_artificial, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_flag, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_accessibility, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data1, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_declaration, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_flag, buffer, pos); + /* This is not in DWARF2 */ + // pos = writeAttrType(DwarfDebugInfo.DW_AT_virtuality, buffer, pos); + // 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) { + pos = writeAttrType(DwarfDebugInfo.DW_AT_object_pointer, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref_addr, buffer, pos); } /* * Now terminate. */ - pos = writeAttrType(DW_AT_null, buffer, pos); - pos = writeAttrForm(DW_FORM_null, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_null, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_null, buffer, pos); return pos; } private int writeFieldDeclarationAbbrevs(DebugContext context, byte[] buffer, int p) { int pos = p; - // instance field no line and file - pos = writeFieldDeclarationAbbrev(context, DW_ABBREV_CODE_field_declaration1, buffer, pos); - // instance field with line and file - pos = writeFieldDeclarationAbbrev(context, DW_ABBREV_CODE_field_declaration2, buffer, pos); - // static field no line and file - pos = writeFieldDeclarationAbbrev(context, DW_ABBREV_CODE_field_declaration3, buffer, pos); - // static field with line and file - pos = writeFieldDeclarationAbbrev(context, DW_ABBREV_CODE_field_declaration4, buffer, pos); + /* An instance field no line and file. */ + pos = writeFieldDeclarationAbbrev(context, DwarfDebugInfo.DW_ABBREV_CODE_field_declaration1, buffer, pos); + /* An instance field with line and file. */ + pos = writeFieldDeclarationAbbrev(context, DwarfDebugInfo.DW_ABBREV_CODE_field_declaration2, buffer, pos); + /* A static field no line and file. */ + pos = writeFieldDeclarationAbbrev(context, DwarfDebugInfo.DW_ABBREV_CODE_field_declaration3, buffer, pos); + /* A static field with line and file. */ + pos = writeFieldDeclarationAbbrev(context, DwarfDebugInfo.DW_ABBREV_CODE_field_declaration4, buffer, pos); return pos; } - private int writeFieldDeclarationAbbrev(DebugContext context, int abbrevCode, byte[] buffer, int p) { + private int writeFieldDeclarationAbbrev(@SuppressWarnings("unused") DebugContext context, int abbrevCode, byte[] buffer, int p) { int pos = p; pos = writeAbbrevCode(abbrevCode, buffer, pos); - pos = writeTag(DW_TAG_member, buffer, pos); - pos = writeFlag(DW_CHILDREN_no, buffer, pos); - pos = writeAttrType(DW_AT_name, buffer, pos); - pos = writeAttrForm(DW_FORM_strp, buffer, pos); - // we may not have a file and line for a field - if (abbrevCode == DW_ABBREV_CODE_field_declaration2 || abbrevCode == DW_ABBREV_CODE_field_declaration4) { - pos = writeAttrType(DW_AT_decl_file, buffer, pos); - pos = writeAttrForm(DW_FORM_data2, buffer, pos); - // at present we definitely don't have line numbers - // pos = writeAttrType(DW_AT_decl_line, buffer, pos); - // pos = writeAttrForm(DW_FORM_data2, buffer, pos); + pos = writeTag(DwarfDebugInfo.DW_TAG_member, buffer, pos); + pos = writeFlag(DwarfDebugInfo.DW_CHILDREN_no, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_name, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_strp, buffer, pos); + /* We may not have a file and line for a field. */ + if (abbrevCode == DwarfDebugInfo.DW_ABBREV_CODE_field_declaration2 || abbrevCode == DwarfDebugInfo.DW_ABBREV_CODE_field_declaration4) { + pos = writeAttrType(DwarfDebugInfo.DW_AT_decl_file, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data2, buffer, pos); + /* At present we definitely don't have line numbers. */ + // pos = writeAttrType(DwarfDebugInfo.DW_AT_decl_line, buffer, pos); + // pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data2, buffer, pos); } - pos = writeAttrType(DW_AT_type, buffer, pos); - pos = writeAttrForm(DW_FORM_ref_addr, buffer, pos); - if (abbrevCode == DW_ABBREV_CODE_field_declaration1 || abbrevCode == DW_ABBREV_CODE_field_declaration2) { - // instance fields have a member offset relocated relative to the heap base register - pos = writeAttrType(DW_AT_data_member_location, buffer, pos); - pos = writeAttrForm(DW_FORM_data2, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_type, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref_addr, buffer, pos); + if (abbrevCode == DwarfDebugInfo.DW_ABBREV_CODE_field_declaration1 || abbrevCode == DwarfDebugInfo.DW_ABBREV_CODE_field_declaration2) { + /* Instance fields have a member offset relocated relative to the heap base register. */ + pos = writeAttrType(DwarfDebugInfo.DW_AT_data_member_location, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data2, buffer, pos); } - pos = writeAttrType(DW_AT_accessibility, buffer, pos); - pos = writeAttrForm(DW_FORM_data1, buffer, pos); - // static fields are only declared here and are external - if (abbrevCode == DW_ABBREV_CODE_field_declaration3 || abbrevCode == DW_ABBREV_CODE_field_declaration4) { - pos = writeAttrType(DW_AT_external, buffer, pos); - pos = writeAttrForm(DW_FORM_flag, buffer, pos); - pos = writeAttrType(DW_AT_declaration, buffer, pos); - pos = writeAttrForm(DW_FORM_flag, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_accessibility, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data1, buffer, pos); + /* Static fields are only declared here and are external. */ + if (abbrevCode == DwarfDebugInfo.DW_ABBREV_CODE_field_declaration3 || abbrevCode == DwarfDebugInfo.DW_ABBREV_CODE_field_declaration4) { + pos = writeAttrType(DwarfDebugInfo.DW_AT_external, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_flag, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_declaration, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_flag, buffer, pos); } /* * Now terminate. */ - pos = writeAttrType(DW_AT_null, buffer, pos); - pos = writeAttrForm(DW_FORM_null, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_null, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_null, buffer, pos); return pos; } - private int writeArrayLayoutAbbrev(DebugContext context, byte[] buffer, int p) { + private int writeArrayLayoutAbbrev(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { int pos = p; - pos = writeAbbrevCode(DW_ABBREV_CODE_array_layout, buffer, pos); - pos = writeTag(DW_TAG_structure_type, buffer, pos); - pos = writeFlag(DW_CHILDREN_yes, buffer, pos); - pos = writeAttrType(DW_AT_name, buffer, pos); - pos = writeAttrForm(DW_FORM_strp, buffer, pos); - pos = writeAttrType(DW_AT_byte_size, buffer, pos); - pos = writeAttrForm(DW_FORM_data2, buffer, pos); + pos = writeAbbrevCode(DwarfDebugInfo.DW_ABBREV_CODE_array_layout, buffer, pos); + pos = writeTag(DwarfDebugInfo.DW_TAG_structure_type, buffer, pos); + pos = writeFlag(DwarfDebugInfo.DW_CHILDREN_yes, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_name, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_strp, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_byte_size, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data2, buffer, pos); + /* Add a data location expression to mask and/or rebase oop pointers. */ + pos = writeAttrType(DwarfDebugInfo.DW_AT_data_location, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_expr_loc, buffer, pos); /* * Now terminate. */ - pos = writeAttrType(DW_AT_null, buffer, pos); - pos = writeAttrForm(DW_FORM_null, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_null, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_null, buffer, pos); return pos; } - private int writeArrayReferenceAbbrev(DebugContext context, byte[] buffer, int p) { + private int writeArrayReferenceAbbrev(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { int pos = p; - pos = writeAbbrevCode(DW_ABBREV_CODE_array_pointer, buffer, pos); - pos = writeTag(DW_TAG_pointer_type, buffer, pos); - pos = writeFlag(DW_CHILDREN_no, buffer, pos); - pos = writeAttrType(DW_AT_byte_size, buffer, pos); - pos = writeAttrForm(DW_FORM_data1, buffer, pos); - pos = writeAttrType(DW_AT_type, buffer, pos); - pos = writeAttrForm(DW_FORM_ref_addr, buffer, pos); + pos = writeAbbrevCode(DwarfDebugInfo.DW_ABBREV_CODE_array_pointer, buffer, pos); + pos = writeTag(DwarfDebugInfo.DW_TAG_pointer_type, buffer, pos); + pos = writeFlag(DwarfDebugInfo.DW_CHILDREN_no, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_byte_size, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data1, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_type, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref_addr, buffer, pos); /* * Now terminate. */ - pos = writeAttrType(DW_AT_null, buffer, pos); - pos = writeAttrForm(DW_FORM_null, buffer, pos); - - // we wll also need a typedef to advertise the array name as the pointer type - pos = writeAbbrevCode(DW_ABBREV_CODE_array_typedef, buffer, pos); - pos = writeTag(DW_TAG_typedef, buffer, pos); - pos = writeFlag(DW_CHILDREN_no, buffer, pos); - pos = writeAttrType(DW_AT_name, buffer, pos); - pos = writeAttrForm(DW_FORM_strp, buffer, pos); - pos = writeAttrType(DW_AT_type, buffer, pos); - pos = writeAttrForm(DW_FORM_ref_addr, buffer, pos); - /* - * Now terminate. - */ - pos = writeAttrType(DW_AT_null, buffer, pos); - pos = writeAttrForm(DW_FORM_null, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_null, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_null, buffer, pos); return pos; } - private int writeInterfaceLayoutAbbrev(DebugContext context, byte[] buffer, int p) { + private int writeInterfaceLayoutAbbrev(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { int pos = p; - pos = writeAbbrevCode(DW_ABBREV_CODE_interface_layout, buffer, pos); - pos = writeTag(DW_TAG_union_type, buffer, pos); - pos = writeFlag(DW_CHILDREN_yes, buffer, pos); - pos = writeAttrType(DW_AT_name, buffer, pos); - pos = writeAttrForm(DW_FORM_strp, buffer, pos); + pos = writeAbbrevCode(DwarfDebugInfo.DW_ABBREV_CODE_interface_layout, buffer, pos); + pos = writeTag(DwarfDebugInfo.DW_TAG_union_type, buffer, pos); + pos = writeFlag(DwarfDebugInfo.DW_CHILDREN_yes, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_name, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_strp, buffer, pos); + /* Add a data location expression to mask and/or rebase oop pointers. */ + pos = writeAttrType(DwarfDebugInfo.DW_AT_data_location, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_expr_loc, buffer, pos); /* * Now terminate. */ - pos = writeAttrType(DW_AT_null, buffer, pos); - pos = writeAttrForm(DW_FORM_null, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_null, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_null, buffer, pos); return pos; } - private int writeInterfaceReferenceAbbrev(DebugContext context, byte[] buffer, int p) { + private int writeInterfaceReferenceAbbrev(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { int pos = p; - pos = writeAbbrevCode(DW_ABBREV_CODE_interface_pointer, buffer, pos); - pos = writeTag(DW_TAG_pointer_type, buffer, pos); - pos = writeFlag(DW_CHILDREN_no, buffer, pos); - pos = writeAttrType(DW_AT_byte_size, buffer, pos); - pos = writeAttrForm(DW_FORM_data1, buffer, pos); - pos = writeAttrType(DW_AT_type, buffer, pos); - pos = writeAttrForm(DW_FORM_ref_addr, buffer, pos); - /* - * Now terminate. - */ - pos = writeAttrType(DW_AT_null, buffer, pos); - pos = writeAttrForm(DW_FORM_null, buffer, pos); - - // we wll also need a typedef to advertise the interface name as the pointer type - pos = writeAbbrevCode(DW_ABBREV_CODE_interface_typedef, buffer, pos); - pos = writeTag(DW_TAG_typedef, buffer, pos); - pos = writeFlag(DW_CHILDREN_no, buffer, pos); - pos = writeAttrType(DW_AT_name, buffer, pos); - pos = writeAttrForm(DW_FORM_strp, buffer, pos); - pos = writeAttrType(DW_AT_type, buffer, pos); - pos = writeAttrForm(DW_FORM_ref_addr, buffer, pos); + pos = writeAbbrevCode(DwarfDebugInfo.DW_ABBREV_CODE_interface_pointer, buffer, pos); + pos = writeTag(DwarfDebugInfo.DW_TAG_pointer_type, buffer, pos); + pos = writeFlag(DwarfDebugInfo.DW_CHILDREN_no, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_byte_size, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data1, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_type, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref_addr, buffer, pos); /* * Now terminate. */ - pos = writeAttrType(DW_AT_null, buffer, pos); - pos = writeAttrForm(DW_FORM_null, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_null, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_null, buffer, pos); return pos; } - private int writeInterfaceImplementorAbbrev(DebugContext context, byte[] buffer, int p) { + private int writeInterfaceImplementorAbbrev(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { int pos = p; - pos = writeAbbrevCode(DW_ABBREV_CODE_interface_implementor, buffer, pos); - pos = writeTag(DW_TAG_member, buffer, pos); - pos = writeFlag(DW_CHILDREN_no, buffer, pos); - pos = writeAttrType(DW_AT_name, buffer, pos); - pos = writeAttrForm(DW_FORM_strp, buffer, pos); - pos = writeAttrType(DW_AT_type, buffer, pos); - pos = writeAttrForm(DW_FORM_ref_addr, buffer, pos); - pos = writeAttrType(DW_AT_accessibility, buffer, pos); - pos = writeAttrForm(DW_FORM_data1, buffer, pos); + pos = writeAbbrevCode(DwarfDebugInfo.DW_ABBREV_CODE_interface_implementor, buffer, pos); + pos = writeTag(DwarfDebugInfo.DW_TAG_member, buffer, pos); + pos = writeFlag(DwarfDebugInfo.DW_CHILDREN_no, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_name, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_strp, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_type, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref_addr, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_accessibility, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data1, buffer, pos); /* * Now terminate. */ - pos = writeAttrType(DW_AT_null, buffer, pos); - pos = writeAttrForm(DW_FORM_null, buffer, pos); - return pos; - } - - private int writeNarrowLayoutAbbrev(DebugContext context, byte[] buffer, int p) { - int pos = p; + pos = writeAttrType(DwarfDebugInfo.DW_AT_null, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_null, buffer, pos); return pos; } - private int writeHeaderFieldAbbrev(DebugContext context, byte[] buffer, int p) { + private int writeHeaderFieldAbbrev(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { int pos = p; - pos = writeAbbrevCode(DW_ABBREV_CODE_header_field, buffer, pos); - pos = writeTag(DW_TAG_member, buffer, pos); - pos = writeFlag(DW_CHILDREN_no, buffer, pos); - pos = writeAttrType(DW_AT_name, buffer, pos); - pos = writeAttrForm(DW_FORM_strp, buffer, pos); - pos = writeAttrType(DW_AT_type, buffer, pos); - pos = writeAttrForm(DW_FORM_ref_addr, buffer, pos); - pos = writeAttrType(DW_AT_data_member_location, buffer, pos); - pos = writeAttrForm(DW_FORM_data1, buffer, pos); - pos = writeAttrType(DW_AT_accessibility, buffer, pos); - pos = writeAttrForm(DW_FORM_data1, buffer, pos); + pos = writeAbbrevCode(DwarfDebugInfo.DW_ABBREV_CODE_header_field, buffer, pos); + pos = writeTag(DwarfDebugInfo.DW_TAG_member, buffer, pos); + pos = writeFlag(DwarfDebugInfo.DW_CHILDREN_no, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_name, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_strp, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_type, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref_addr, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_data_member_location, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data1, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_accessibility, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data1, buffer, pos); /* * Now terminate. */ - pos = writeAttrType(DW_AT_null, buffer, pos); - pos = writeAttrForm(DW_FORM_null, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_null, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_null, buffer, pos); return pos; } - private int writeArrayDataTypeAbbrev(DebugContext context, byte[] buffer, int p) { + private int writeArrayDataTypeAbbrev(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { int pos = p; - pos = writeAbbrevCode(DW_ABBREV_CODE_array_data_type, buffer, pos); - pos = writeTag(DW_TAG_array_type, buffer, pos); - pos = writeFlag(DW_CHILDREN_no, buffer, pos); - pos = writeAttrType(DW_AT_byte_size, buffer, pos); - pos = writeAttrForm(DW_FORM_data1, buffer, pos); - pos = writeAttrType(DW_AT_type, buffer, pos); - pos = writeAttrForm(DW_FORM_ref_addr, buffer, pos); + pos = writeAbbrevCode(DwarfDebugInfo.DW_ABBREV_CODE_array_data_type, buffer, pos); + pos = writeTag(DwarfDebugInfo.DW_TAG_array_type, buffer, pos); + pos = writeFlag(DwarfDebugInfo.DW_CHILDREN_no, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_byte_size, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data1, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_type, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref_addr, buffer, pos); /* * Now terminate. */ - pos = writeAttrType(DW_AT_null, buffer, pos); - pos = writeAttrForm(DW_FORM_null, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_null, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_null, buffer, pos); return pos; } - private int writeMethodLocationAbbrev(DebugContext context, byte[] buffer, int p) { + private int writeMethodLocationAbbrev(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { int pos = p; - pos = writeAbbrevCode(DW_ABBREV_CODE_method_location, buffer, pos); - pos = writeTag(DW_TAG_subprogram, buffer, pos); - pos = writeFlag(DW_CHILDREN_yes, buffer, pos); - pos = writeAttrType(DW_AT_low_pc, buffer, pos); - pos = writeAttrForm(DW_FORM_addr, buffer, pos); - pos = writeAttrType(DW_AT_hi_pc, buffer, pos); - pos = writeAttrForm(DW_FORM_addr, buffer, pos); - pos = writeAttrType(DW_AT_external, buffer, pos); - pos = writeAttrForm(DW_FORM_flag, buffer, pos); - pos = writeAttrType(DW_AT_specification, buffer, pos); - pos = writeAttrForm(DW_FORM_ref_addr, 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(DW_AT_null, buffer, pos); - pos = writeAttrForm(DW_FORM_null, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_null, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_null, buffer, pos); return pos; } - private int writeStaticFieldLocationAbbrev(DebugContext context, byte[] buffer, int p) { + private int writeStaticFieldLocationAbbrev(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { int pos = p; - pos = writeAbbrevCode(DW_ABBREV_CODE_static_field_location, buffer, pos); - pos = writeTag(DW_TAG_variable, buffer, pos); - pos = writeFlag(DW_CHILDREN_no, buffer, pos); - pos = writeAttrType(DW_AT_specification, buffer, pos); - pos = writeAttrForm(DW_FORM_ref_addr, buffer, pos); - // pos = writeAttrType(DW_AT_linkage_name, buffer, pos); - // pos = writeAttrForm(DW_FORM_strp, buffer, pos); - pos = writeAttrType(DW_AT_location, buffer, pos); - pos = writeAttrForm(DW_FORM_expr_loc, buffer, pos); + pos = writeAbbrevCode(DwarfDebugInfo.DW_ABBREV_CODE_static_field_location, buffer, pos); + pos = writeTag(DwarfDebugInfo.DW_TAG_variable, buffer, pos); + pos = writeFlag(DwarfDebugInfo.DW_CHILDREN_no, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_specification, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref_addr, buffer, pos); + /* Do we have a symbol name to use here? n.b. this is not in DWARF2. */ + // pos = writeAttrType(DwarfDebugInfo.DW_AT_linkage_name, buffer, pos); + // pos = writeAttrForm(DwarfDebugInfo.DW_FORM_strp, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_location, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_expr_loc, buffer, pos); /* * Now terminate. */ - pos = writeAttrType(DW_AT_null, buffer, pos); - pos = writeAttrForm(DW_FORM_null, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_null, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_null, buffer, pos); return pos; } - private int writeSuperReferenceAbbrev(DebugContext context, byte[] buffer, int p) { + private int writeSuperReferenceAbbrev(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { int pos = p; - pos = writeAbbrevCode(DW_ABBREV_CODE_super_reference, buffer, pos); - pos = writeTag(DW_TAG_inheritance, buffer, pos); - pos = writeFlag(DW_CHILDREN_no, buffer, pos); - pos = writeAttrType(DW_AT_type, buffer, pos); - pos = writeAttrForm(DW_FORM_ref_addr, buffer, pos); - pos = writeAttrType(DW_AT_data_member_location, buffer, pos); - pos = writeAttrForm(DW_FORM_data1, buffer, pos); // = offset? in which segment though? - pos = writeAttrType(DW_AT_accessibility, buffer, pos); - pos = writeAttrForm(DW_FORM_data1, buffer, pos); // = offset? in which segment though? + pos = writeAbbrevCode(DwarfDebugInfo.DW_ABBREV_CODE_super_reference, buffer, pos); + pos = writeTag(DwarfDebugInfo.DW_TAG_inheritance, buffer, pos); + pos = writeFlag(DwarfDebugInfo.DW_CHILDREN_no, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_type, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref_addr, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_data_member_location, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data1, buffer, pos); // = offset? in which + // segment though? + pos = writeAttrType(DwarfDebugInfo.DW_AT_accessibility, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data1, buffer, pos); // = offset? in which + // segment though? /* * Now terminate. */ - pos = writeAttrType(DW_AT_null, buffer, pos); - pos = writeAttrForm(DW_FORM_null, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_null, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_null, buffer, pos); return pos; } private int writeParameterDeclarationAbbrevs(DebugContext context, byte[] buffer, int p) { int pos = p; - pos = writeParameterDeclarationAbbrev(context, DW_ABBREV_CODE_method_parameter_declaration1, buffer, pos); - pos = writeParameterDeclarationAbbrev(context, DW_ABBREV_CODE_method_parameter_declaration2, buffer, pos); - pos = writeParameterDeclarationAbbrev(context, DW_ABBREV_CODE_method_parameter_declaration3, buffer, pos); + pos = writeParameterDeclarationAbbrev(context, DwarfDebugInfo.DW_ABBREV_CODE_method_parameter_declaration1, buffer, pos); + pos = writeParameterDeclarationAbbrev(context, DwarfDebugInfo.DW_ABBREV_CODE_method_parameter_declaration2, buffer, pos); + pos = writeParameterDeclarationAbbrev(context, DwarfDebugInfo.DW_ABBREV_CODE_method_parameter_declaration3, buffer, pos); return pos; } - private int writeParameterDeclarationAbbrev(DebugContext context, int abbrevCode, byte[] buffer, int p) { + private int writeParameterDeclarationAbbrev(@SuppressWarnings("unused") DebugContext context, int abbrevCode, byte[] buffer, int p) { int pos = p; pos = writeAbbrevCode(abbrevCode, buffer, pos); - pos = writeTag(DW_TAG_formal_parameter, buffer, pos); - pos = writeFlag(DW_CHILDREN_no, buffer, pos); - // pos = writeAttrType(DW_AT_name, buffer, pos); - // pos = writeAttrForm(DW_FORM_strp, buffer, pos); - if (abbrevCode == DW_ABBREV_CODE_method_parameter_declaration2) { - // don't always have a file name and line numbers are not yet available - pos = writeAttrType(DW_AT_decl_file, buffer, pos); - pos = writeAttrForm(DW_FORM_data2, buffer, pos); - // pos = writeAttrType(DW_AT_decl_line, buffer, pos); - // pos = writeAttrForm(DW_FORM_data2, buffer, pos); + pos = writeTag(DwarfDebugInfo.DW_TAG_formal_parameter, buffer, pos); + pos = writeFlag(DwarfDebugInfo.DW_CHILDREN_no, buffer, pos); + /* We don't yet have parameter names. */ + // pos = writeAttrType(DwarfDebugInfo.DW_AT_name, buffer, pos); + // pos = writeAttrForm(DwarfDebugInfo.DW_FORM_strp, buffer, pos); + if (abbrevCode == DwarfDebugInfo.DW_ABBREV_CODE_method_parameter_declaration2) { + /* Line numbers for parameter declarations are not (yet?) available. */ + pos = writeAttrType(DwarfDebugInfo.DW_AT_decl_file, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data2, buffer, pos); + // pos = writeAttrType(DwarfDebugInfo.DW_AT_decl_line, buffer, pos); + // pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data2, buffer, pos); } - pos = writeAttrType(DW_AT_type, buffer, pos); - pos = writeAttrForm(DW_FORM_ref_addr, buffer, pos); - if (abbrevCode == DW_ABBREV_CODE_method_parameter_declaration1) { - // only this parameter is artificial and it has no line - pos = writeAttrType(DW_AT_artificial, buffer, pos); - pos = writeAttrForm(DW_FORM_flag, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_type, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref_addr, buffer, pos); + if (abbrevCode == DwarfDebugInfo.DW_ABBREV_CODE_method_parameter_declaration1) { + /* Only this parameter is artificial and it has no line. */ + pos = writeAttrType(DwarfDebugInfo.DW_AT_artificial, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_flag, buffer, pos); } - // don't yet have locations for method parameters - // not even at the start of the method - // pos = writeAttrType(DW_AT_location, buffer, pos); - // pos = writeAttrForm(DW_FORM_data4, buffer, pos); + /*- + * We don't yet have locations for method parameters, + * not even at the start of the method. + */ + // pos = writeAttrType(DwarfDebugInfo.DW_AT_location, buffer, pos); + // pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data4, buffer, pos); /* * Now terminate. */ - pos = writeAttrType(DW_AT_null, buffer, pos); - pos = writeAttrForm(DW_FORM_null, buffer, pos); + 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. */ - private static final String TARGET_SECTION_NAME = DW_ARANGES_SECTION_NAME; + private static final String TARGET_SECTION_NAME = DwarfDebugInfo.DW_ARANGES_SECTION_NAME; @Override public String targetSectionName() { 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 0848607e168b..8f30d867768f 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 @@ -64,41 +64,38 @@ public class DwarfDebugInfo extends DebugInfoBase { * Define all the abbrev section codes we need for our DIEs. */ @SuppressWarnings("unused") public static final int DW_ABBREV_CODE_null = 0; - // level 0 DIEs + /* Level 0 DIEs. */ 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; - // level 1 DIEs + /* 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_layout = 8; public static final int DW_ABBREV_CODE_class_pointer = 9; - public static final int DW_ABBREV_CODE_class_typedef = 10; - public static final int DW_ABBREV_CODE_method_location = 11; - 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_array_typedef = 15; - public static final int DW_ABBREV_CODE_interface_layout = 16; - public static final int DW_ABBREV_CODE_interface_pointer = 17; - public static final int DW_ABBREV_CODE_interface_typedef = 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_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; - // 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; + public static final int DW_ABBREV_CODE_method_location = 10; + public static final int DW_ABBREV_CODE_static_field_location = 11; + public static final int DW_ABBREV_CODE_array_layout = 12; + public static final int DW_ABBREV_CODE_array_pointer = 13; + public static final int DW_ABBREV_CODE_interface_layout = 14; + public static final int DW_ABBREV_CODE_interface_pointer = 15; + /* Level 2 DIEs. */ + public static final int DW_ABBREV_CODE_method_declaration1 = 16; + public static final int DW_ABBREV_CODE_method_declaration2 = 17; + public static final int DW_ABBREV_CODE_field_declaration1 = 18; + public static final int DW_ABBREV_CODE_field_declaration2 = 19; + public static final int DW_ABBREV_CODE_field_declaration3 = 20; + public static final int DW_ABBREV_CODE_field_declaration4 = 21; + public static final int DW_ABBREV_CODE_header_field = 22; + public static final int DW_ABBREV_CODE_array_data_type = 23; + public static final int DW_ABBREV_CODE_super_reference = 24; + public static final int DW_ABBREV_CODE_interface_implementor = 25; + /* Level 3 DIEs. */ + public static final int DW_ABBREV_CODE_method_parameter_declaration1 = 26; + public static final int DW_ABBREV_CODE_method_parameter_declaration2 = 27; + public static final int DW_ABBREV_CODE_method_parameter_declaration3 = 28; /* * Define all the Dwarf tags we need for our DIEs. */ @@ -109,7 +106,6 @@ public class DwarfDebugInfo extends DebugInfoBase { public static final int DW_TAG_pointer_type = 0x0f; public static final int DW_TAG_compile_unit = 0x11; public static final int DW_TAG_structure_type = 0x13; - public static final int DW_TAG_typedef = 0x16; public static final int DW_TAG_union_type = 0x17; public static final int DW_TAG_inheritance = 0x1c; public static final int DW_TAG_base_type = 0x24; @@ -134,9 +130,9 @@ public class DwarfDebugInfo extends DebugInfoBase { 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; - // public static final int DW_AT_decl_column = 0x39; + @SuppressWarnings("unused") public static final int DW_AT_decl_column = 0x39; public static final int DW_AT_decl_file = 0x3a; - // public static final int DW_AT_decl_line = 0x3b; + @SuppressWarnings("unused") public static final int DW_AT_decl_line = 0x3b; public static final int DW_AT_declaration = 0x3c; public static final int DW_AT_encoding = 0x3e; public static final int DW_AT_external = 0x3f; @@ -144,7 +140,8 @@ public class DwarfDebugInfo extends DebugInfoBase { @SuppressWarnings("unused") public static final int DW_AT_frame_base = 0x40; public static final int DW_AT_specification = 0x47; public static final int DW_AT_type = 0x49; - public static final int Dw_AT_object_pointer = 0x64; + public static final int DW_AT_data_location = 0x50; + public static final int DW_AT_object_pointer = 0x64; /* * Define all the Dwarf attribute forms we need for our DIEs. @@ -233,14 +230,22 @@ public class DwarfDebugInfo extends DebugInfoBase { * Values used to build DWARF expressions and locations */ public static final byte DW_OP_addr = 0x03; + @SuppressWarnings("unused") public static final byte DW_OP_deref = 0x06; + public static final byte DW_OP_dup = 0x12; + public static final byte DW_OP_and = 0x1a; + public static final byte DW_OP_not = 0x20; + public static final byte DW_OP_shl = 0x20; public static final byte DW_OP_plus = 0x22; + public static final byte DW_OP_bra = 0x28; + public static final byte DW_OP_eq = 0x29; + public static final byte DW_OP_lit0 = 0x30; public static final byte DW_OP_breg0 = 0x70; public static final byte DW_OP_push_object_address = (byte) 0x97; - // register constants for AArch64 + /* Register constants for AArch64. */ public static final byte rheapbase_aarch64 = (byte) 27; public static final byte rthread_aarch64 = (byte) 28; - // register constants for x86 + /* Register constants for x86. */ public static final byte rheapbase_x86 = (byte) 14; public static final byte rthread_x86 = (byte) 15; @@ -369,11 +374,6 @@ static class DwarfClassProperties extends DwarfTypeProperties { * Index of the class entry's class_layout DIE in the debug_info section. */ private int layoutIndex; - /** - * Index of the class entry's pointer type for the class_layout DIE in the debug_info - * section. - */ - private int pointerIndex; /** * Index into debug_line section for associated compilation unit. */ @@ -400,7 +400,6 @@ static class DwarfClassProperties extends DwarfTypeProperties { this.cuIndex = -1; this.deoptCUIndex = -1; this.layoutIndex = -1; - this.pointerIndex = -1; this.lineIndex = -1; this.linePrologueSize = -1; this.lineSectionSize = -1; @@ -458,6 +457,7 @@ private DwarfTypeProperties lookupTypeProperties(String typeName) { return typeProperties; } + @SuppressWarnings("unused") private DwarfClassProperties lookupClassProperties(String typeName) { DwarfTypeProperties classProperties = propertiesIndex.get(typeName); assert classProperties != null; @@ -528,19 +528,6 @@ int getLayoutIndex(ClassEntry classEntry) { return classProperties.layoutIndex; } - void setPointerIndex(ClassEntry classEntry, int idx) { - DwarfClassProperties classProperties = lookupClassProperties(classEntry); - assert classProperties.getTypeEntry() == classEntry; - assert classProperties.pointerIndex == -1 || classProperties.pointerIndex == idx; - classProperties.pointerIndex = idx; - } - - int getPointerIndex(String typeName) { - DwarfClassProperties classProperties = lookupClassProperties(typeName); - assert classProperties.pointerIndex >= 0; - return classProperties.pointerIndex; - } - void setLineIndex(ClassEntry classEntry, int idx) { DwarfClassProperties classProperties; classProperties = lookupClassProperties(classEntry); @@ -553,7 +540,7 @@ public int getLineIndex(ClassEntry classEntry) { DwarfClassProperties classProperties; classProperties = lookupClassProperties(classEntry); assert classProperties.getTypeEntry() == classEntry; - // line index may be fetched without being set + /* line index may be fetched without being set */ assert classProperties.lineIndex >= -1; return classProperties.lineIndex; } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImpl.java index b131a054a8eb..7ac1cd3b0194 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImpl.java @@ -35,21 +35,6 @@ import java.util.List; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_CFA_CIE_id; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_CFA_CIE_version; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_CFA_advance_loc; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_CFA_advance_loc1; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_CFA_advance_loc2; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_CFA_advance_loc4; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_CFA_def_cfa; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_CFA_def_cfa_offset; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_CFA_nop; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_CFA_offset; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_CFA_register; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_CFA_restore; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_FRAME_SECTION_NAME; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_LINE_SECTION_NAME; - /** * Section generic generator for debug_frame section. */ @@ -63,7 +48,7 @@ public DwarfFrameSectionImpl(DwarfDebugInfo dwarfSections) { @Override public String getSectionName() { - return DW_FRAME_SECTION_NAME; + return DwarfDebugInfo.DW_FRAME_SECTION_NAME; } @Override @@ -133,9 +118,9 @@ private int writeCIE(byte[] buffer, int p) { */ int pos = p; if (buffer == null) { - pos += putInt(0, scratch, 0); // don't care about length - pos += putInt(DW_CFA_CIE_id, scratch, 0); - pos += putByte(DW_CFA_CIE_version, scratch, 0); + pos += putInt(0, scratch, 0); + pos += putInt(DwarfDebugInfo.DW_CFA_CIE_id, scratch, 0); + pos += putByte(DwarfDebugInfo.DW_CFA_CIE_version, scratch, 0); pos += putAsciiStringBytes("", scratch, 0); pos += putULEB(1, scratch, 0); pos += putSLEB(-8, scratch, 0); @@ -155,8 +140,8 @@ private int writeCIE(byte[] buffer, int p) { } else { int lengthPos = pos; pos = putInt(0, buffer, pos); - pos = putInt(DW_CFA_CIE_id, buffer, pos); - pos = putByte(DW_CFA_CIE_version, buffer, pos); + pos = putInt(DwarfDebugInfo.DW_CFA_CIE_id, buffer, pos); + pos = putByte(DwarfDebugInfo.DW_CFA_CIE_version, buffer, pos); pos = putAsciiStringBytes("", buffer, pos); pos = putULEB(1, buffer, pos); pos = putSLEB(-8, buffer, pos); @@ -259,7 +244,7 @@ private int writePaddingNops(byte[] buffer, int p) { if (buffer == null) { pos++; } else { - pos = putByte(DW_CFA_nop, buffer, pos); + pos = putByte(DwarfDebugInfo.DW_CFA_nop, buffer, pos); } } return pos; @@ -268,11 +253,11 @@ private int writePaddingNops(byte[] buffer, int p) { protected int writeDefCFA(int register, int offset, byte[] buffer, int p) { int pos = p; if (buffer == null) { - pos += putByte(DW_CFA_def_cfa, scratch, 0); + pos += putByte(DwarfDebugInfo.DW_CFA_def_cfa, scratch, 0); pos += putSLEB(register, scratch, 0); return pos + putULEB(offset, scratch, 0); } else { - pos = putByte(DW_CFA_def_cfa, buffer, pos); + pos = putByte(DwarfDebugInfo.DW_CFA_def_cfa, buffer, pos); pos = putULEB(register, buffer, pos); return putULEB(offset, buffer, pos); } @@ -281,10 +266,10 @@ protected int writeDefCFA(int register, int offset, byte[] buffer, int p) { protected int writeDefCFAOffset(int offset, byte[] buffer, int p) { int pos = p; if (buffer == null) { - pos += putByte(DW_CFA_def_cfa_offset, scratch, 0); + pos += putByte(DwarfDebugInfo.DW_CFA_def_cfa_offset, scratch, 0); return pos + putULEB(offset, scratch, 0); } else { - pos = putByte(DW_CFA_def_cfa_offset, buffer, pos); + pos = putByte(DwarfDebugInfo.DW_CFA_def_cfa_offset, buffer, pos); return putULEB(offset, buffer, pos); } } @@ -312,7 +297,7 @@ protected int writeAdvanceLoc0(byte offset, byte[] buffer, int pos) { protected int writeAdvanceLoc1(byte offset, byte[] buffer, int p) { int pos = p; - byte op = DW_CFA_advance_loc1; + byte op = DwarfDebugInfo.DW_CFA_advance_loc1; if (buffer == null) { pos += putByte(op, scratch, 0); return pos + putByte(offset, scratch, 0); @@ -323,7 +308,7 @@ protected int writeAdvanceLoc1(byte offset, byte[] buffer, int p) { } protected int writeAdvanceLoc2(short offset, byte[] buffer, int p) { - byte op = DW_CFA_advance_loc2; + byte op = DwarfDebugInfo.DW_CFA_advance_loc2; int pos = p; if (buffer == null) { pos += putByte(op, scratch, 0); @@ -335,7 +320,7 @@ protected int writeAdvanceLoc2(short offset, byte[] buffer, int p) { } protected int writeAdvanceLoc4(int offset, byte[] buffer, int p) { - byte op = DW_CFA_advance_loc4; + byte op = DwarfDebugInfo.DW_CFA_advance_loc4; int pos = p; if (buffer == null) { pos += putByte(op, scratch, 0); @@ -372,11 +357,11 @@ protected int writeRestore(int register, byte[] buffer, int p) { protected int writeRegister(int savedReg, int savedToReg, byte[] buffer, int p) { int pos = p; if (buffer == null) { - pos += putByte(DW_CFA_register, scratch, 0); + pos += putByte(DwarfDebugInfo.DW_CFA_register, scratch, 0); pos += putULEB(savedReg, scratch, 0); return pos + putULEB(savedToReg, scratch, 0); } else { - pos = putByte(DW_CFA_register, buffer, pos); + pos = putByte(DwarfDebugInfo.DW_CFA_register, buffer, pos); pos = putULEB(savedReg, buffer, pos); return putULEB(savedToReg, buffer, pos); } @@ -392,7 +377,7 @@ protected int writeRegister(int savedReg, int savedToReg, byte[] buffer, int p) /** * The debug_frame section depends on debug_line section. */ - private static final String TARGET_SECTION_NAME = DW_LINE_SECTION_NAME; + private static final String TARGET_SECTION_NAME = DwarfDebugInfo.DW_LINE_SECTION_NAME; @Override public String targetSectionName() { @@ -411,16 +396,16 @@ public LayoutDecision.Kind[] targetSectionKinds() { private static byte offsetOp(int register) { assert (register >> 6) == 0; - return (byte) ((DW_CFA_offset << 6) | register); + return (byte) ((DwarfDebugInfo.DW_CFA_offset << 6) | register); } private static byte restoreOp(int register) { assert (register >> 6) == 0; - return (byte) ((DW_CFA_restore << 6) | register); + return (byte) ((DwarfDebugInfo.DW_CFA_restore << 6) | register); } private static byte advanceLoc0Op(int offset) { assert (offset >= 0 && offset <= 0x3f); - return (byte) ((DW_CFA_advance_loc << 6) | offset); + return (byte) ((DwarfDebugInfo.DW_CFA_advance_loc << 6) | offset); } } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImplAArch64.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImplAArch64.java index 6d53fcf2979c..06eeeadfaa0a 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImplAArch64.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfFrameSectionImplAArch64.java @@ -38,7 +38,7 @@ public class DwarfFrameSectionImplAArch64 extends DwarfFrameSectionImpl { public static final int DW_CFA_FP_IDX = 29; private static final int DW_CFA_LR_IDX = 30; private static final int DW_CFA_SP_IDX = 31; - // private static final int DW_CFA_PC_IDX = 32; + @SuppressWarnings("unused") private static final int DW_CFA_PC_IDX = 32; public DwarfFrameSectionImplAArch64(DwarfDebugInfo dwarfSections) { super(dwarfSections); 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 aa88674897a1..c84f90f09ef6 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 @@ -49,55 +49,7 @@ import java.util.Map; import java.util.Set; -import static com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugPrimitiveTypeInfo.FLAG_INTEGRAL; -import static com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugPrimitiveTypeInfo.FLAG_NUMERIC; -import static com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugPrimitiveTypeInfo.FLAG_SIGNED; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_array_data_type; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_array_layout; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_array_pointer; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_array_typedef; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_array_unit; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_builtin_unit; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_class_layout; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_class_pointer; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_class_typedef; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_class_unit1; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_class_unit2; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_field_declaration1; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_field_declaration2; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_field_declaration3; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_field_declaration4; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_header_field; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_interface_implementor; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_interface_layout; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_interface_pointer; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_interface_typedef; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_method_declaration1; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_method_declaration2; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_method_location; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_method_parameter_declaration1; -// import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_method_parameter_declaration2; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_method_parameter_declaration3; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_object_header; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_primitive_type; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_static_field_location; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_super_reference; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ABBREV_CODE_void_type; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ACCESS_private; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ACCESS_protected; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ACCESS_public; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ATE_boolean; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ATE_float; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ATE_signed; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ATE_signed_char; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_ATE_unsigned; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_FLAG_true; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_INFO_SECTION_NAME; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_LANG_Java; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_OP_addr; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_OP_breg0; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_VERSION_4; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.TEXT_SECTION_NAME; +import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugPrimitiveTypeInfo; /** * Section generator for debug_info section. @@ -124,15 +76,17 @@ public DwarfInfoSectionImpl(DwarfDebugInfo dwarfSections) { @Override public String getSectionName() { - return DW_INFO_SECTION_NAME; + return DwarfDebugInfo.DW_INFO_SECTION_NAME; } @Override public Set getDependencies(Map decisions) { Set deps = super.getDependencies(decisions); LayoutDecision ourContent = decisions.get(getElement()).getDecision(LayoutDecision.Kind.CONTENT); - // order all content decisions after all size decisions by - // making info section content depend on abbrev section size + /* + * Order all content decisions after all size decisions by making info section content + * depend on abbrev section size. + */ String abbrevSectionName = dwarfSections.getAbbrevSectionImpl().getSectionName(); ELFObjectFile.ELFSection abbrevSection = (ELFObjectFile.ELFSection) getElement().getOwner().elementForName(abbrevSectionName); LayoutDecision sizeDecision = decisions.get(abbrevSection).getDecision(LayoutDecision.Kind.SIZE); @@ -169,47 +123,50 @@ public void writeContent(DebugContext context) { byte computeEncoding(int flags, int bitCount) { assert bitCount > 0; - if ((flags & FLAG_NUMERIC) != 0) { - if (((flags & FLAG_INTEGRAL) != 0)) { - if ((flags & FLAG_SIGNED) != 0) { + if ((flags & DebugPrimitiveTypeInfo.FLAG_NUMERIC) != 0) { + if (((flags & DebugPrimitiveTypeInfo.FLAG_INTEGRAL) != 0)) { + if ((flags & DebugPrimitiveTypeInfo.FLAG_SIGNED) != 0) { switch (bitCount) { case 8: - return DW_ATE_signed_char; + return DwarfDebugInfo.DW_ATE_signed_char; default: assert bitCount == 16 || bitCount == 32 || bitCount == 64; - return DW_ATE_signed; + return DwarfDebugInfo.DW_ATE_signed; } } else { assert bitCount == 16; - return DW_ATE_unsigned; // should be UTF??? + return DwarfDebugInfo.DW_ATE_unsigned; } } else { assert bitCount == 32 || bitCount == 64; - return DW_ATE_float; + return DwarfDebugInfo.DW_ATE_float; } } else { assert bitCount == 1; - return DW_ATE_boolean; + return DwarfDebugInfo.DW_ATE_boolean; } } public int generateContent(DebugContext context, byte[] buffer) { int pos = 0; - pos = writeBuiltInUnit(context, buffer, pos); - // write entries for all the types known to the generator + /* Write entries for all the types known to the generator. */ + + pos = writeBuiltInUnit(context, buffer, pos); - // write class units for non-primary classes i.e. ones which - // don't have associated methods + /* + * Write class units for non-primary classes i.e. ones which don't have associated methods. + */ pos = writeNonPrimaryClasses(context, buffer, pos); - // write class units for primary classes in increasing order - // of method address + /* + * Write class units for primary classes in increasing order of method address. + */ pos = writePrimaryClasses(context, buffer, pos); - // write class units for array types + /* Write class units for array types. */ pos = writeArrayTypes(context, buffer, pos); @@ -240,44 +197,45 @@ public int generateContent(DebugContext context, byte[] buffer) { return pos; } - public int writeBuiltInUnit(DebugContext context, byte[] buffer, int pos) { + public int writeBuiltInUnit(DebugContext context, byte[] buffer, int p) { + int pos = p; int lengthPos = pos; log(context, " [0x%08x] <0> builtin unit", pos); pos = writeCUHeader(buffer, pos); assert pos == lengthPos + DW_DIE_HEADER_SIZE; - int abbrevCode = DW_ABBREV_CODE_builtin_unit; + int abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_builtin_unit; 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"); - pos = writeAttrData1(DW_LANG_Java, buffer, pos); + pos = writeAttrData1(DwarfDebugInfo.DW_LANG_Java, buffer, pos); - // write child entries for basic Java types + /* Write child entries for basic Java types. */ pos = getTypes().filter(TypeEntry::isPrimitive).reduce(pos, - (p, typeEntry) -> { + (pos1, typeEntry) -> { PrimitiveTypeEntry primitiveTypeEntry = (PrimitiveTypeEntry) typeEntry; if (primitiveTypeEntry.getBitCount() > 0) { - return writePrimitiveType(context, primitiveTypeEntry, buffer, p); + return writePrimitiveType(context, primitiveTypeEntry, buffer, pos1); } else { - return writeVoidType(context, primitiveTypeEntry, buffer, p); + return writeVoidType(context, primitiveTypeEntry, buffer, pos1); } }, (oldpos, newpos) -> newpos); - // write child entries for object header and array header structs + /* Write child entries for object header and array header structs. */ pos = getTypes().filter(TypeEntry::isHeader).reduce(pos, - (p, typeEntry) -> { + (pos1, typeEntry) -> { HeaderTypeEntry headerTypeEntry = (HeaderTypeEntry) typeEntry; - return writeHeaderType(context, headerTypeEntry, buffer, p); + return writeHeaderType(context, headerTypeEntry, buffer, pos1); }, (oldpos, newpos) -> newpos); - // terminate with null entry + /* Terminate with null entry. */ pos = writeAttrNull(buffer, pos); - // fix up the CU length + /* Fix up the CU length. */ patchLength(lengthPos, buffer, pos); @@ -288,9 +246,9 @@ public int writePrimitiveType(DebugContext context, PrimitiveTypeEntry primitive assert primitiveTypeEntry.getBitCount() > 0; int pos = p; log(context, " [0x%08x] primitive type %s", pos, primitiveTypeEntry.getTypeName()); - // record the location of this type entry + /* Record the location of this type entry. */ setTypeIndex(primitiveTypeEntry, pos); - int abbrevCode = DW_ABBREV_CODE_primitive_type; + int abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_primitive_type; log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); pos = writeAbbrevCode(abbrevCode, buffer, pos); byte byteSize = (byte) primitiveTypeEntry.getSize(); @@ -311,9 +269,9 @@ public int writeVoidType(DebugContext context, PrimitiveTypeEntry primitiveTypeE assert primitiveTypeEntry.getBitCount() == 0; int pos = p; log(context, " [0x%08x] primitive type void", pos); - // record the location of this type entry + /* Record the location of this type entry. */ setTypeIndex(primitiveTypeEntry, pos); - int abbrevCode = DW_ABBREV_CODE_void_type; + int abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_void_type; log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); pos = writeAbbrevCode(abbrevCode, buffer, pos); String name = primitiveTypeEntry.getTypeName(); @@ -326,16 +284,18 @@ public int writeHeaderType(DebugContext context, HeaderTypeEntry headerTypeEntry String name = headerTypeEntry.getTypeName(); byte size = (byte) headerTypeEntry.getSize(); log(context, " [0x%08x] header type %s", pos, name); - // record the location of this type entry + /* Record the location of this type entry. */ setTypeIndex(headerTypeEntry, pos); - int abbrevCode = DW_ABBREV_CODE_object_header; + int abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_object_header; log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); pos = writeAbbrevCode(abbrevCode, buffer, pos); log(context, " [0x%08x] name 0x%x (%s)", pos, debugStringIndex(name), name); pos = writeAttrStrp(name, buffer, pos); log(context, " [0x%08x] byte_size 0x%x", pos, size); pos = writeAttrData1(size, buffer, pos); - + /* Write a data location expression to mask and/or rebase oop pointers. */ + log(context, " [0x%08x] data_location", pos); + pos = writeOopRelocationExpression(buffer, pos); pos = writeHeaderFields(context, headerTypeEntry, buffer, pos); /* * Write a terminating null attribute. @@ -356,7 +316,7 @@ private int writeHeaderField(DebugContext context, FieldEntry fieldEntry, byte[] String valueTypeName = valueType.getTypeName(); int valueTypeIdx = getTypeIndex(valueTypeName); log(context, " [0x%08x] header field", pos); - int abbrevCode = DW_ABBREV_CODE_header_field; + int abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_header_field; log(context, " [0x%08x] <2> Abbrev Number %d", pos, abbrevCode); pos = writeAbbrevCode(abbrevCode, buffer, pos); log(context, " [0x%08x] name 0x%x (%s)", pos, debugStringIndex(fieldName), fieldName); @@ -390,28 +350,30 @@ private int writeNonPrimaryClassUnit(DebugContext context, ClassEntry classEntry log(context, " [0x%08x] non primary class unit %s", pos, classEntry.getTypeName()); 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 = DW_ABBREV_CODE_class_unit2; + /* Non-primary classes have no compiled methods so they also have no line section entry. */ + int abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_class_unit2; 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"); - pos = writeAttrData1(DW_LANG_Java, buffer, pos); + pos = writeAttrData1(DwarfDebugInfo.DW_LANG_Java, buffer, pos); log(context, " [0x%08x] name 0x%x (%s)", pos, debugStringIndex(classEntry.getFileName()), classEntry.getFileName()); pos = writeAttrStrp(classEntry.getFileName(), buffer, pos); String compilationDirectory = classEntry.getCachePath(); log(context, " [0x%08x] comp_dir 0x%x (%s)", pos, debugStringIndex(compilationDirectory), compilationDirectory); pos = writeAttrStrp(compilationDirectory, buffer, pos); - // lo and hi should really be optional + /* 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); - // n.b. there is no need to write a stmt_list (line section idx) for this class unit - // as the class has no code + /* + * 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 + /* Now write the child DIEs starting with the layout and pointer type. */ if (classEntry.isInterface()) { InterfaceClassEntry interfaceClassEntry = (InterfaceClassEntry) classEntry; @@ -422,17 +384,17 @@ private int writeNonPrimaryClassUnit(DebugContext context, ClassEntry classEntry pos = writeClassType(context, classEntry, buffer, pos); } - // for a non-primary there are no method definitions to write + /* Note, for a non-primary there are no method definitions to write. */ - // write all static field definitions + /* Write all static field definitions. */ pos = writeStaticFieldLocations(context, classEntry, buffer, pos); - // terminate children with null entry + /* Terminate children with null entry. */ pos = writeAttrNull(buffer, pos); - // fix up the CU length + /* Fix up the CU length. */ patchLength(lengthPos, buffer, pos); @@ -453,8 +415,8 @@ 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 - int abbrevCode = (fileName.length() > 0 ? DW_ABBREV_CODE_class_unit1 : DW_ABBREV_CODE_class_unit2); + /* Primary classes only have a line section entry if they have an associated file. */ + int abbrevCode = (fileName.length() > 0 ? DwarfDebugInfo.DW_ABBREV_CODE_class_unit1 : DwarfDebugInfo.DW_ABBREV_CODE_class_unit2); setCUIndex(classEntry, pos); int lengthPos = pos; log(context, " [0x%08x] primary class unit %s", pos, classEntry.getTypeName()); @@ -463,28 +425,30 @@ private int writePrimaryClassUnit(DebugContext context, ClassEntry classEntry, b 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"); - pos = writeAttrData1(DW_LANG_Java, buffer, pos); + pos = writeAttrData1(DwarfDebugInfo.DW_LANG_Java, buffer, pos); log(context, " [0x%08x] name 0x%x (%s)", pos, debugStringIndex(fileName), fileName); pos = writeAttrStrp(fileName, buffer, pos); String compilationDirectory = classEntry.getCachePath(); log(context, " [0x%08x] comp_dir 0x%x (%s)", pos, debugStringIndex(compilationDirectory), compilationDirectory); pos = writeAttrStrp(compilationDirectory, buffer, pos); LinkedList 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 + /* + * 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 - if (abbrevCode == DW_ABBREV_CODE_class_unit1) { + /* Only write stmt_list if the entry actually has line number info. */ + if (abbrevCode == DwarfDebugInfo.DW_ABBREV_CODE_class_unit1) { log(context, " [0x%08x] stmt_list 0x%08x", pos, lineIndex); pos = writeAttrData4(lineIndex, buffer, pos); } - // now write the child DIEs starting with the layout and pointer type + /* Now write the child DIEs starting with the layout and pointer type. */ if (classEntry.isInterface()) { InterfaceClassEntry interfaceClassEntry = (InterfaceClassEntry) classEntry; @@ -495,19 +459,19 @@ private int writePrimaryClassUnit(DebugContext context, ClassEntry classEntry, b pos = writeClassType(context, classEntry, buffer, pos); } - // write all method locations + /* Write all method locations. */ pos = writeMethodLocations(context, classEntry, buffer, pos); - // write all static field definitions + /* Write all static field definitions. */ pos = writeStaticFieldLocations(context, classEntry, buffer, pos); - // terminate children with null entry + /* Terminate children with null entry. */ pos = writeAttrNull(buffer, pos); - // fix up the CU length + /* Fix up the CU length. */ patchLength(lengthPos, buffer, pos); @@ -518,10 +482,10 @@ private int writeClassLayout(DebugContext context, ClassEntry classEntry, byte[] int pos = p; setLayoutIndex(classEntry, pos); log(context, " [0x%08x] class layout", pos); - int abbrevCode = DW_ABBREV_CODE_class_layout; + int abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_class_layout; log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); pos = writeAbbrevCode(abbrevCode, buffer, pos); - String name = uniqueDebugString("_" + classEntry.getTypeName()); + String name = classEntry.getTypeName(); log(context, " [0x%08x] name 0x%x (%s)", pos, debugStringIndex(name), name); pos = writeAttrStrp(name, buffer, pos); int size = classEntry.getSize(); @@ -530,22 +494,22 @@ private int writeClassLayout(DebugContext context, ClassEntry classEntry, byte[] int fileIdx = classEntry.localFilesIdx(classEntry.getFileEntry()); log(context, " [0x%08x] file 0x%x (%s)", pos, fileIdx, classEntry.getFileName()); pos = writeAttrData2((short) fileIdx, buffer, pos); - ClassEntry superClassEntry = classEntry.getSuperClass(); - // n.b. the containing_type attribute is not strict DWARF but gdb expects it - // we also add an inheritance member with the same info + /* Write a data location expression to mask and/or rebase oop pointers. */ + log(context, " [0x%08x] data_location", pos); + pos = writeOopRelocationExpression(buffer, pos); int superTypeOffset; String superName; + ClassEntry superClassEntry = classEntry.getSuperClass(); if (superClassEntry != null) { - // inherit layout from super class + /* Inherit layout from super class. */ superName = superClassEntry.getTypeName(); superTypeOffset = getLayoutIndex(superClassEntry); } else { - // inherit layout from object header + /* Inherit layout from object header. */ superName = OBJECT_HEADER_STRUCT_NAME; superTypeOffset = getTypeIndex(superName); } - log(context, " [0x%08x] containing_type 0x%x (%s)", pos, superTypeOffset, superName); - pos = writeAttrRefAddr(superTypeOffset, buffer, pos); + /* Now write the child fields. */ pos = writeSuperReference(context, superTypeOffset, superName, buffer, pos); pos = writeFields(context, classEntry, buffer, pos); pos = writeMethodDeclarations(context, classEntry, buffer, pos); @@ -555,14 +519,15 @@ private int writeClassLayout(DebugContext context, ClassEntry classEntry, byte[] return writeAttrNull(buffer, pos); } - private int writeSuperReference(DebugContext context, int superTypeOffset, String superName, byte[] buffer, int pos) { + private int writeSuperReference(DebugContext context, int superTypeOffset, String superName, byte[] buffer, int p) { + int pos = p; log(context, " [0x%08x] super reference", pos); - int abbrevCode = DW_ABBREV_CODE_super_reference; + int abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_super_reference; log(context, " [0x%08x] <2> Abbrev Number %d", pos, abbrevCode); pos = writeAbbrevCode(abbrevCode, buffer, pos); log(context, " [0x%08x] type 0x%x (%s)", pos, superTypeOffset, superName); pos = writeAttrRefAddr(superTypeOffset, buffer, pos); - // parent layout is embedded at start of object + /* Parent layout is embedded at start of object. */ log(context, " [0x%08x] data_member_location (super) 0x%x", pos, 0); pos = writeAttrData1((byte) 0, buffer, pos); log(context, " [0x%08x] modifiers public", pos); @@ -590,17 +555,17 @@ private int writeField(DebugContext context, ClassEntry classEntry, FieldEntry f boolean isStatic = Modifier.isStatic(modifiers); if (!isStatic) { if (!hasFile) { - abbrevCode = DW_ABBREV_CODE_field_declaration1; + abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_field_declaration1; } else { - abbrevCode = DW_ABBREV_CODE_field_declaration2; + abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_field_declaration2; } } else { if (!hasFile) { - abbrevCode = DW_ABBREV_CODE_field_declaration3; + abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_field_declaration3; } else { - abbrevCode = DW_ABBREV_CODE_field_declaration4; + abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_field_declaration4; } - // record the position of the declaration to use when we write the definition + /* Record the position of the declaration to use when we write the definition. */ setFieldDeclarationIndex(classEntry, fieldEntry.fieldName(), pos); } log(context, " [0x%08x] <2> Abbrev Number %d", pos, abbrevCode); @@ -609,15 +574,15 @@ private int writeField(DebugContext context, ClassEntry classEntry, FieldEntry f String name = fieldEntry.fieldName(); log(context, " [0x%08x] name 0x%x (%s)", pos, debugStringIndex(name), name); pos = writeAttrStrp(name, buffer, pos); - // we may not have a file and line for a field + /* We may not have a file and line for a field. */ if (hasFile) { int fileIdx = classEntry.localFilesIdx(classEntry.getFileEntry()); log(context, " [0x%08x] filename 0x%x (%s)", pos, fileIdx, classEntry.getFileName()); pos = writeAttrData2((short) fileIdx, buffer, pos); - // at present we definitely don't have line numbers + /* At present we definitely don't have line numbers. */ } String valueTypeName = fieldEntry.getValueType().getTypeName(); - // static fields never store compressed values instance fields may do + /* Static fields never store compressed values instance fields may do. */ int typeIdx = getTypeIndex(valueTypeName); log(context, " [0x%08x] type 0x%x (%s)", pos, typeIdx, valueTypeName); pos = writeAttrRefAddr(typeIdx, buffer, pos); @@ -628,7 +593,7 @@ private int writeField(DebugContext context, ClassEntry classEntry, FieldEntry f } log(context, " [0x%08x] accessibility %s", pos, fieldEntry.getModifiersString()); pos = writeAttrAccessibility(fieldEntry.getModifiers(), buffer, pos); - // static fields are only declared here and are external + /* Static fields are only declared here and are external. */ if (isStatic) { log(context, " [0x%08x] external(true)", pos); pos = writeFlag((byte) 1, buffer, pos); @@ -643,8 +608,10 @@ private int writeMethodDeclarations(DebugContext context, ClassEntry classEntry, LinkedList classPrimaryEntries = classEntry.getPrimaryEntries(); for (PrimaryEntry primaryEntry : classPrimaryEntries) { Range range = primaryEntry.getPrimary(); - // declare all methods including deopt targets even though they are written in separate - // CUs. + /* + * Declare all methods including deopt targets even though they are written in separate + * CUs. + */ pos = writeMethodDeclaration(context, classEntry, range, buffer, pos); } @@ -653,12 +620,12 @@ private int writeMethodDeclarations(DebugContext context, ClassEntry classEntry, private int writeMethodDeclaration(DebugContext context, ClassEntry classEntry, Range range, byte[] buffer, int p) { int pos = p; - String methodKey = range.getFullMethodNameWithParamsAndReturnType(); + String methodKey = range.getSymbolName(); setMethodDeclarationIndex(classEntry, methodKey, pos); int modifiers = range.getModifiers(); boolean isStatic = Modifier.isStatic(modifiers); log(context, " [0x%08x] method declaration %s", pos, methodKey); - int abbrevCode = (isStatic ? DW_ABBREV_CODE_method_declaration2 : DW_ABBREV_CODE_method_declaration1); + int abbrevCode = (isStatic ? DwarfDebugInfo.DW_ABBREV_CODE_method_declaration2 : DwarfDebugInfo.DW_ABBREV_CODE_method_declaration1); log(context, " [0x%08x] <2> Abbrev Number %d", pos, abbrevCode); pos = writeAbbrevCode(abbrevCode, buffer, pos); log(context, " [0x%08x] external true", pos); @@ -682,16 +649,20 @@ 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 == DW_ABBREV_CODE_method_declaration1) { - // record the current position so we can back patch the object pointer + if (abbrevCode == DwarfDebugInfo.DW_ABBREV_CODE_method_declaration1) { + /* Record the current position so we can back patch the object pointer. */ int objectPointerIndex = pos; - // write a dummy ref address to move pos on to where the first parameter gets written + /* + * Write a dummy ref address to move pos on to where the first parameter gets written. + */ pos = writeAttrRefAddr(0, buffer, pos); - // now backpatch object pointer slot with current pos, identifying the first parameter + /* + * Now backpatch object pointer slot with current pos, identifying the first parameter. + */ log(context, " [0x%08x] object_pointer 0x%x", objectPointerIndex, pos); writeAttrRefAddr(pos, buffer, objectPointerIndex); } - // write method parameter declarations + /* Write method parameter declarations. */ pos = writeMethodParameterDeclarations(context, classEntry, range, true, buffer, pos); /* * Write a terminating null attribute. @@ -721,24 +692,24 @@ private int writeMethodParameterDeclarations(DebugContext context, ClassEntry cl return pos; } - private int writeMethodParameterDeclaration(DebugContext context, String paramName, String paramTypeName, boolean artificial, boolean isSpecification, byte[] buffer, int p) { + private int writeMethodParameterDeclaration(DebugContext context, @SuppressWarnings("unused") String paramName, String paramTypeName, boolean artificial, boolean isSpecification, byte[] buffer, + int p) { int pos = p; log(context, " [0x%08x] method parameter declaration", pos); int abbrevCode; int level = (isSpecification ? 3 : 2); if (artificial) { - abbrevCode = DW_ABBREV_CODE_method_parameter_declaration1; + abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_method_parameter_declaration1; } else { - abbrevCode = DW_ABBREV_CODE_method_parameter_declaration3; + abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_method_parameter_declaration3; } log(context, " [0x%08x] <%d> Abbrev Number %d", pos, level, abbrevCode); pos = writeAbbrevCode(abbrevCode, buffer, pos); - // an artificial 'this' parameter has to be typed using the raw pointer type. - // other parameters can be typed using the typedef that retains the Java type name - int typeIdx = (artificial ? getPointerIndex(paramTypeName) : getTypeIndex(paramTypeName)); + /* We don't have parameter names at present. */ + int typeIdx = getTypeIndex(paramTypeName); log(context, " [0x%08x] type 0x%x (%s)", pos, typeIdx, paramTypeName); pos = writeAttrRefAddr(typeIdx, buffer, pos); - if (abbrevCode == DW_ABBREV_CODE_method_parameter_declaration1) { + if (abbrevCode == DwarfDebugInfo.DW_ABBREV_CODE_method_parameter_declaration1) { log(context, " [0x%08x] artificial true", pos); pos = writeFlag((byte) 1, buffer, pos); } @@ -750,13 +721,15 @@ private int writeInterfaceLayout(DebugContext context, InterfaceClassEntry inter int layoutOffset = pos; setLayoutIndex(interfaceClassEntry, layoutOffset); log(context, " [0x%08x] interface layout", pos); - int abbrevCode = DW_ABBREV_CODE_interface_layout; + int abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_interface_layout; log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); pos = writeAbbrevCode(abbrevCode, buffer, pos); - String name = uniqueDebugString("_" + interfaceClassEntry.getTypeName()); + String name = interfaceClassEntry.getTypeName(); log(context, " [0x%08x] name 0x%x (%s)", pos, debugStringIndex(name), name); pos = writeAttrStrp(name, buffer, pos); - + /* Write a data location expression to mask and/or rebase oop pointers. */ + log(context, " [0x%08x] data_location", pos); + pos = writeOopRelocationExpression(buffer, pos); /* * now write references to all class layouts that implement this interface */ @@ -778,7 +751,7 @@ private int writeInterfaceImplementors(DebugContext context, InterfaceClassEntry private int writeInterfaceImplementor(DebugContext context, ClassEntry classEntry, byte[] buffer, int p) { int pos = p; log(context, " [0x%08x] interface implementor", pos); - int abbrevCode = DW_ABBREV_CODE_interface_implementor; + int abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_interface_implementor; log(context, " [0x%08x] <2> Abbrev Number %d", pos, abbrevCode); pos = writeAbbrevCode(abbrevCode, buffer, pos); String name = uniqueDebugString("_" + classEntry.getTypeName()); @@ -795,64 +768,38 @@ private int writeInterfaceImplementor(DebugContext context, ClassEntry classEntr private int writeClassType(DebugContext context, ClassEntry classEntry, byte[] buffer, int p) { int pos = p; - String name = classEntry.getTypeName(); - int pointerTypeOffset = pos; - // define a pointer type referring to the underlying layout - setPointerIndex(classEntry, pos); + /* Define a pointer type referring to the underlying layout. */ + setTypeIndex(classEntry, pos); log(context, " [0x%08x] class pointer type", pos); - int abbrevCode = DW_ABBREV_CODE_class_pointer; + int abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_class_pointer; log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); pos = writeAbbrevCode(abbrevCode, buffer, pos); - log(context, " [0x%08x] byte_size 0x%x", pos, 8); - pos = writeAttrData1((byte) 8, buffer, pos); + int byteSize = dwarfSections.oopReferenceByteCount(); + log(context, " [0x%08x] byte_size 0x%x", pos, byteSize); + pos = writeAttrData1((byte) byteSize, buffer, pos); int layoutOffset = getLayoutIndex(classEntry); log(context, " [0x%08x] type 0x%x", pos, layoutOffset); pos = writeAttrRefAddr(layoutOffset, buffer, pos); - // now write a typedef to name the pointer type and use it as the defining type - // for the Java class type name - setTypeIndex(classEntry, pos); - log(context, " [0x%08x] class pointer typedef", pos); - abbrevCode = DW_ABBREV_CODE_class_typedef; - log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); - pos = writeAbbrevCode(abbrevCode, buffer, pos); - log(context, " [0x%08x] name 0x%x (%s)", pos, debugStringIndex(name), name); - pos = writeAttrStrp(name, buffer, pos); - log(context, " [0x%08x] type (typedef) 0x%x (%s)", pos, pointerTypeOffset, name); - pos = writeAttrRefAddr(pointerTypeOffset, buffer, pos); - return pos; } private int writeInterfaceType(DebugContext context, InterfaceClassEntry interfaceClassEntry, byte[] buffer, int p) { int pos = p; - String name = interfaceClassEntry.getTypeName(); - int pointerTypeOffset = pos; - // define a pointer type referring to the underlying layout - setPointerIndex(interfaceClassEntry, pos); + /* Define a pointer type referring to the underlying layout. */ + setTypeIndex(interfaceClassEntry, pos); log(context, " [0x%08x] interface pointer type", pos); - int abbrevCode = DW_ABBREV_CODE_interface_pointer; + int abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_interface_pointer; log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); pos = writeAbbrevCode(abbrevCode, buffer, pos); - log(context, " [0x%08x] byte_size 0x%x", pos, 8); - pos = writeAttrData1((byte) 8, buffer, pos); + int byteSize = dwarfSections.oopReferenceByteCount(); + log(context, " [0x%08x] byte_size 0x%x", pos, byteSize); + pos = writeAttrData1((byte) byteSize, buffer, pos); int layoutOffset = getLayoutIndex(interfaceClassEntry); log(context, " [0x%08x] type 0x%x", pos, layoutOffset); pos = writeAttrRefAddr(layoutOffset, buffer, pos); - // now write a typedef to name the pointer type and use it as the defining type - // for the Java array type name - - setTypeIndex(interfaceClassEntry, pos); - log(context, " [0x%08x] interface pointer typedef", pos); - abbrevCode = DW_ABBREV_CODE_interface_typedef; - log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); - pos = writeAbbrevCode(abbrevCode, buffer, pos); - log(context, " [0x%08x] name 0x%x (%s)", pos, debugStringIndex(name), name); - pos = writeAttrStrp(name, buffer, pos); - log(context, " [0x%08x] type (typedef) 0x%x (%s)", pos, pointerTypeOffset, name); - pos = writeAttrRefAddr(pointerTypeOffset, buffer, pos); return pos; } @@ -871,9 +818,10 @@ private int writeMethodLocations(DebugContext context, ClassEntry classEntry, by } private int writeStaticFieldLocations(DebugContext context, ClassEntry classEntry, byte[] buffer, int p) { - // only write locations for static fields that have an offset greater than 0. - // a negative offset indicates that the field has been folded into code as - // an unmaterialized constant. + /* + * Only write locations for static fields that have an offset greater than 0. A negative + * offset indicates that the field has been folded into code as an unmaterialized constant. + */ return classEntry.fields().filter(DwarfInfoSectionImpl::isManifestedStaticField).reduce(p, (pos, fieldEntry) -> writeStaticFieldLocation(context, classEntry, fieldEntry, buffer, pos), (oldPos, newPos) -> newPos); @@ -888,12 +836,12 @@ private int writeStaticFieldLocation(DebugContext context, ClassEntry classEntry String fieldName = fieldEntry.fieldName(); int fieldDefinitionOffset = getFieldDeclarationIndex(classEntry, fieldName); log(context, " [0x%08x] static field location %s.%s", pos, classEntry.getTypeName(), fieldName); - int abbrevCode = DW_ABBREV_CODE_static_field_location; + int abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_static_field_location; log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); pos = writeAbbrevCode(abbrevCode, buffer, pos); log(context, " [0x%08x] specification 0x%x", pos, fieldDefinitionOffset); pos = writeAttrRefAddr(fieldDefinitionOffset, buffer, pos); - // field offset needs to be relocated relative to static primitive or static object base + /* Field offset needs to be relocated relative to static primitive or static object base. */ int offset = fieldEntry.getOffset(); log(context, " [0x%08x] location heapbase + 0x%x (%s)", pos, offset, (fieldEntry.getValueType().isPrimitive() ? "primitive" : "object")); pos = writeHeapLocation(offset, buffer, pos); @@ -903,25 +851,27 @@ private int writeStaticFieldLocation(DebugContext context, ClassEntry classEntry private int writeHeapLocation(int offset, byte[] buffer, int p) { int pos = p; if (dwarfSections.useHeapBase()) { - // write a location rebasing the offset relative to the heapbase register - byte regOp = (byte) (DW_OP_breg0 + dwarfSections.getHeapbaseRegister()); - // we have to size the DWARF expression by writing it to the scratch buffer - // so we can write its size as a ULEB before the expression itself + /* Write a location rebasing the offset relative to the heapbase register. */ + byte regOp = (byte) (DwarfDebugInfo.DW_OP_breg0 + dwarfSections.getHeapbaseRegister()); + /* + * We have to size the DWARF expression by writing it to the scratch buffer so we can + * write its size as a ULEB before the expression itself. + */ int size = putByte(regOp, scratch, 0) + putSLEB(offset, scratch, 0); if (buffer == null) { - // add ULEB size to the expression size + /* Add ULEB size to the expression size. */ return pos + putULEB(size, scratch, 0) + size; } else { - // write the size and expression into the output buffer + /* Write the size and expression into the output buffer. */ pos = putULEB(size, buffer, pos); pos = putByte(regOp, buffer, pos); return putSLEB(offset, buffer, pos); } } else { - // write a relocatable address relative to the heap section start - byte regOp = DW_OP_addr; + /* Write a relocatable address relative to the heap section start. */ + byte regOp = DwarfDebugInfo.DW_OP_addr; int size = 9; - // write the size and expression into the output buffer + /* Write the size and expression into the output buffer. */ if (buffer == null) { return pos + putULEB(size, scratch, 0) + size; } else { @@ -948,15 +898,15 @@ private int writeArrayTypeUnit(DebugContext context, ArrayTypeEntry arrayTypeEnt log(context, " [0x%08x] array class unit %s", pos, arrayTypeEntry.getTypeName()); pos = writeCUHeader(buffer, pos); assert pos == lengthPos + DW_DIE_HEADER_SIZE; - int abbrevCode = DW_ABBREV_CODE_array_unit; + int abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_array_unit; 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"); - pos = writeAttrData1(DW_LANG_Java, buffer, pos); + log(context, " [0x%08x] language %s", pos, "DwarfDebugInfo.DW_LANG_Java"); + pos = writeAttrData1(DwarfDebugInfo.DW_LANG_Java, buffer, pos); String name = arrayTypeEntry.getTypeName(); log(context, " [0x%08x] name 0x%x (%s)", pos, debugStringIndex(name), name); pos = writeAttrStrp(name, buffer, pos); - // write the array layout and array reference DIEs + /* Write the array layout and array reference DIEs. */ int layouIdx = pos; pos = writeArrayLayout(context, arrayTypeEntry, buffer, pos); pos = writeArrayType(context, arrayTypeEntry, layouIdx, buffer, pos); @@ -965,7 +915,7 @@ private int writeArrayTypeUnit(DebugContext context, ArrayTypeEntry arrayTypeEnt */ pos = writeAttrNull(buffer, pos); - // fix up the CU length + /* Fix up the CU length. */ patchLength(lengthPos, buffer, pos); return pos; @@ -982,20 +932,25 @@ private int writeArrayLayout(DebugContext context, ArrayTypeEntry arrayTypeEntry headerType = (StructureTypeEntry) lookupType(ARRAY_HEADER_STRUCT_NAME + "A"); } log(context, " [0x%08x] array layout", pos); - int abbrevCode = DW_ABBREV_CODE_array_layout; + int abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_array_layout; log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); pos = writeAbbrevCode(abbrevCode, buffer, pos); - String name = uniqueDebugString("_" + arrayTypeEntry.getTypeName()); + String name = arrayTypeEntry.getTypeName(); log(context, " [0x%08x] name 0x%x (%s)", pos, debugStringIndex(name), name); pos = writeAttrStrp(name, buffer, pos); int size = headerType.getSize(); log(context, " [0x%08x] byte_size 0x%x", pos, size); pos = writeAttrData2((short) size, buffer, pos); - // now the child DIEs - // write a type definition for the element array field + /* Write a data location expression to mask and/or rebase oop pointers. */ + log(context, " [0x%08x] data_location", pos); + pos = writeOopRelocationExpression(buffer, pos); + + /* Now the child DIEs. */ + + /* write a type definition for the element array field. */ int arrayDataTypeIdx = pos; pos = writeArrayDataType(context, elementType, buffer, pos); - // write a zero length element array field + /* Write a zero length element array field. */ pos = writeArrayElementField(context, size, arrayDataTypeIdx, buffer, pos); pos = writeArraySuperReference(context, arrayTypeEntry, buffer, pos); /* @@ -1007,7 +962,7 @@ private int writeArrayLayout(DebugContext context, ArrayTypeEntry arrayTypeEntry private int writeArrayDataType(DebugContext context, TypeEntry elementType, byte[] buffer, int p) { int pos = p; log(context, " [0x%08x] array element data type", pos); - int abbrevCode = DW_ABBREV_CODE_array_data_type; + int abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_array_data_type; log(context, " [0x%08x] <2> Abbrev Number %d", pos, abbrevCode); pos = writeAbbrevCode(abbrevCode, buffer, pos); int size = (elementType.isPrimitive() ? elementType.getSize() : 8); @@ -1023,7 +978,7 @@ private int writeArrayDataType(DebugContext context, TypeEntry elementType, byte private int writeArrayElementField(DebugContext context, int offset, int arrayDataTypeIdx, byte[] buffer, int p) { int pos = p; log(context, " [0x%08x] array element data field", pos); - int abbrevCode = DW_ABBREV_CODE_header_field; + int abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_header_field; log(context, " [0x%08x] <2> Abbrev Number %d", pos, abbrevCode); pos = writeAbbrevCode(abbrevCode, buffer, pos); String fieldName = uniqueDebugString("data"); @@ -1040,7 +995,8 @@ private int writeArrayElementField(DebugContext context, int offset, int arrayDa } - private int writeArraySuperReference(DebugContext context, ArrayTypeEntry arrayTypeEntry, byte[] buffer, int pos) { + private int writeArraySuperReference(DebugContext context, ArrayTypeEntry arrayTypeEntry, byte[] buffer, int p) { + int pos = p; String headerName; TypeEntry elementType = arrayTypeEntry.getElementType(); if (elementType.isPrimitive()) { @@ -1050,12 +1006,12 @@ private int writeArraySuperReference(DebugContext context, ArrayTypeEntry arrayT } int headerTypeOffset = getTypeIndex(headerName); log(context, " [0x%08x] super reference", pos); - int abbrevCode = DW_ABBREV_CODE_super_reference; + int abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_super_reference; log(context, " [0x%08x] <2> Abbrev Number %d", pos, abbrevCode); pos = writeAbbrevCode(abbrevCode, buffer, pos); log(context, " [0x%08x] type 0x%x (%s)", pos, headerTypeOffset, headerName); pos = writeAttrRefAddr(headerTypeOffset, buffer, pos); - // parent layout is embedded at start of object + /* Parent layout is embedded at start of object. */ log(context, " [0x%08x] data_member_location (super) 0x%x", pos, 0); pos = writeAttrData1((byte) 0, buffer, pos); log(context, " [0x%08x] modifiers public", pos); @@ -1066,31 +1022,20 @@ private int writeArraySuperReference(DebugContext context, ArrayTypeEntry arrayT private int writeArrayType(DebugContext context, ArrayTypeEntry arrayTypeEntry, int layoutOffset, byte[] buffer, int p) { int pos = p; - int pointerTypeOffset = pos; String name = uniqueDebugString(arrayTypeEntry.getTypeName()); - // define a pointer type referring to the underlying layout + setTypeIndex(arrayTypeEntry, pos); + /* Define a pointer type referring to the underlying layout. */ log(context, " [0x%08x] array pointer type", pos); - int abbrevCode = DW_ABBREV_CODE_array_pointer; + int abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_array_pointer; log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); pos = writeAbbrevCode(abbrevCode, buffer, pos); - log(context, " [0x%08x] byte_size 0x%x", pos, 8); - pos = writeAttrData1((byte) 8, buffer, pos); + int byteSize = dwarfSections.oopReferenceByteCount(); + log(context, " [0x%08x] byte_size 0x%x", pos, byteSize); + pos = writeAttrData1((byte) byteSize, buffer, pos); log(context, " [0x%08x] type (pointer) 0x%x (%s)", pos, layoutOffset, name); pos = writeAttrRefAddr(layoutOffset, buffer, pos); - // now write a typedef to name the pointer type and use it as the defining type - // for the Java array type name - setTypeIndex(arrayTypeEntry, pos); - log(context, " [0x%08x] array pointer typedef", pos); - abbrevCode = DW_ABBREV_CODE_array_typedef; - log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); - pos = writeAbbrevCode(abbrevCode, buffer, pos); - log(context, " [0x%08x] name 0x%x (%s)", pos, debugStringIndex(name), name); - pos = writeAttrStrp(name, buffer, pos); - log(context, " [0x%08x] type (typedef) 0x%x (%s)", pos, pointerTypeOffset, name); - pos = writeAttrRefAddr(pointerTypeOffset, buffer, pos); - return pos; } @@ -1100,11 +1045,11 @@ private int writeDeoptMethodsCU(DebugContext context, ClassEntry classEntry, byt LinkedList classPrimaryEntries = classEntry.getPrimaryEntries(); String fileName = classEntry.getFileName(); int lineIndex = getLineIndex(classEntry); - int abbrevCode = (fileName.length() > 0 ? DW_ABBREV_CODE_class_unit1 : DW_ABBREV_CODE_class_unit2); + 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); - log(context, " [0x%08x] language %s", pos, "DW_LANG_Java"); - pos = writeAttrData1(DW_LANG_Java, buffer, pos); + log(context, " [0x%08x] language %s", pos, "DwarfDebugInfo.DW_LANG_Java"); + pos = writeAttrData1(DwarfDebugInfo.DW_LANG_Java, buffer, pos); log(context, " [0x%08x] name 0x%x (%s)", pos, debugStringIndex(classEntry.getFileName()), classEntry.getFileName()); pos = writeAttrStrp(classEntry.getFileName(), buffer, pos); String compilationDirectory = classEntry.getCachePath(); @@ -1116,7 +1061,7 @@ private int writeDeoptMethodsCU(DebugContext context, ClassEntry classEntry, byt pos = writeAttrAddress(lo, buffer, pos); log(context, " [0x%08x] hi_pc 0x%08x", pos, hi); pos = writeAttrAddress(hi, buffer, pos); - if (abbrevCode == DW_ABBREV_CODE_class_unit1) { + if (abbrevCode == DwarfDebugInfo.DW_ABBREV_CODE_class_unit1) { log(context, " [0x%08x] stmt_list 0x%08x", pos, lineIndex); pos = writeAttrData4(lineIndex, buffer, pos); } @@ -1136,7 +1081,7 @@ 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 = 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()); @@ -1147,8 +1092,8 @@ private int writeMethodLocation(DebugContext context, ClassEntry classEntry, Ran * Should pass true only if method is non-private. */ log(context, " [0x%08x] external true", pos); - pos = writeFlag(DW_FLAG_true, buffer, pos); - String methodKey = range.getFullMethodNameWithParamsAndReturnType(); + pos = writeFlag(DwarfDebugInfo.DW_FLAG_true, buffer, pos); + String methodKey = range.getSymbolName(); int methodSpecOffset = getMethodDeclarationIndex(classEntry, methodKey); log(context, " [0x%08x] specification 0x%x (%s)", pos, methodSpecOffset, methodKey); pos = writeAttrRefAddr(methodSpecOffset, buffer, pos); @@ -1165,7 +1110,7 @@ private int writeCUHeader(byte[] buffer, int p) { /* CU length. */ pos += putInt(0, scratch, 0); /* DWARF version. */ - pos += putShort(DW_VERSION_4, scratch, 0); + pos += putShort(DwarfDebugInfo.DW_VERSION_4, scratch, 0); /* Abbrev offset. */ pos += putInt(0, scratch, 0); /* Address size. */ @@ -1174,7 +1119,7 @@ private int writeCUHeader(byte[] buffer, int p) { /* CU length. */ pos = putInt(0, buffer, pos); /* DWARF version. */ - pos = putShort(DW_VERSION_4, buffer, pos); + pos = putShort(DwarfDebugInfo.DW_VERSION_4, buffer, pos); /* Abbrev offset. */ pos = putInt(0, buffer, pos); /* Address size. */ @@ -1195,7 +1140,7 @@ private static int findLo(LinkedList classPrimaryEntries, boolean } } } - // we should never get here + /* We should never get here. */ assert false; return 0; } @@ -1216,7 +1161,7 @@ private static int findHi(LinkedList classPrimaryEntries, boolean } } } - // should never get here + /* We should never get here. */ assert false; return 0; } @@ -1244,22 +1189,96 @@ public int writeAttrString(String value, byte[] buffer, int p) { public int writeAttrAccessibility(int modifiers, byte[] buffer, int p) { byte access; if (Modifier.isPublic(modifiers)) { - access = DW_ACCESS_public; + access = DwarfDebugInfo.DW_ACCESS_public; } else if (Modifier.isProtected(modifiers)) { - access = DW_ACCESS_protected; + access = DwarfDebugInfo.DW_ACCESS_protected; } else if (Modifier.isPrivate(modifiers)) { - access = DW_ACCESS_private; + access = DwarfDebugInfo.DW_ACCESS_private; } else { - // package private -- make it public for now - access = DW_ACCESS_public; + /* Actually package private -- make it public for now. */ + access = DwarfDebugInfo.DW_ACCESS_public; } return writeAttrData1(access, buffer, p); } + public int writeOopRelocationExpression(byte[] buffer, int p) { + int pos = p; + if (dwarfSections.useHeapBase()) { + /*- + * oop is 32 bit signed offset, possibly shifted by CompressEncoding.getShift() + * + * .... push object address (1 byte) ...... [offset] + * .... duplicate object base ............. [offset, offset] + * .... breq end .......................... [offset] + * .... __optional_begin__ (if shift != 0) + * .... push compress_shift ............... [offset, shift] + * .... lsh ............................... [new_offset] + * .... __optional_end__ + * .... push rheap + 0 .................... [rheap] + * .... ADD ............................... [oop] + * end: ................................... [offset=0 | oop] + */ + + /* Write a location rebasing the offset relative to the heapbase register. */ + byte regOp = (byte) (DwarfDebugInfo.DW_OP_breg0 + dwarfSections.getHeapbaseRegister()); + /* + * We have to size the DWARF expression by writing it to the scratch buffer so we can + * write its size as a ULEB before the expression itself. + */ + int shiftBitCount = dwarfSections.oopShiftBitCount(); + short skipBytes = (short) (shiftBitCount == 0 ? 3 : 5); + int size = 7 + skipBytes; + if (buffer == null) { + /* add ULEB size to the expression size. */ + return pos + putULEB(size, scratch, 0) + size; + } else { + /* write the size and expression into the output buffer. */ + pos = putULEB(size, buffer, pos); + pos = putByte(DwarfDebugInfo.DW_OP_push_object_address, buffer, pos); + pos = putByte(DwarfDebugInfo.DW_OP_dup, buffer, pos); + pos = putByte(DwarfDebugInfo.DW_OP_lit0, buffer, pos); + pos = putByte(DwarfDebugInfo.DW_OP_eq, buffer, pos); + pos = putByte(DwarfDebugInfo.DW_OP_bra, buffer, pos); + pos = putShort(skipBytes, buffer, pos); + if (shiftBitCount > 0) { + putByte((byte) (DwarfDebugInfo.DW_OP_lit0 + shiftBitCount), buffer, pos); + putByte(DwarfDebugInfo.DW_OP_shl, buffer, pos); + } + pos = putByte(regOp, buffer, pos); + pos = putSLEB(0, buffer, pos); /* 1 byte. */ + return putByte(DwarfDebugInfo.DW_OP_plus, buffer, pos); + } + } else { + /*- + * oop is 64 bit pointer modulo low flag bits + * guaranteed: mask == 2^N - 1 where N <= 5 + * + * .... push object address .. [tagged_oop] + * .... push flag bits mask .. [tagged_oop, flag_bits_mask] + * .... NOT .................. [tagged_oop, oop_bits_mask] + * .... AND .................. [oop] + */ + + /* Write a relocatable address relative to the heap section start. */ + int size = 4; + /* Write the size and expression into the output buffer. */ + if (buffer == null) { + return pos + putULEB(size, scratch, 0) + size; + } else { + pos = putULEB(size, buffer, pos); + pos = putByte(DwarfDebugInfo.DW_OP_push_object_address, buffer, pos); + pos = putByte((byte) (DwarfDebugInfo.DW_OP_lit0 + dwarfSections.oopFlagBitsMask()), buffer, pos); + pos = putByte(DwarfDebugInfo.DW_OP_not, buffer, pos); + pos = putByte(DwarfDebugInfo.DW_OP_and, buffer, pos); + } + return pos; + } + } + /** * The debug_info section depends on abbrev section. */ - protected static final String TARGET_SECTION_NAME = TEXT_SECTION_NAME; + protected static final String TARGET_SECTION_NAME = DwarfDebugInfo.TEXT_SECTION_NAME; @Override public String targetSectionName() { 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 1e804d314719..444d0ad4545f 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 @@ -39,10 +39,6 @@ import java.util.Map; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_LINE_SECTION_NAME; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_STR_SECTION_NAME; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_VERSION_2; - /** * Section generator for debug_line section. */ @@ -138,7 +134,7 @@ public class DwarfLineSectionImpl extends DwarfSectionImpl { @Override public String getSectionName() { - return DW_LINE_SECTION_NAME; + return DwarfDebugInfo.DW_LINE_SECTION_NAME; } @Override @@ -321,7 +317,7 @@ private int writeHeader(ClassEntry classEntry, byte[] buffer, int p) { /* * 2 ubyte version is always 2. */ - pos = putShort(DW_VERSION_2, buffer, pos); + pos = putShort(DwarfDebugInfo.DW_VERSION_2, buffer, pos); /* * 4 ubyte prologue length includes rest of header and dir + file table section. */ @@ -915,7 +911,7 @@ private static boolean isFixedAdvancePC(long addressDiff) { /** * The debug_line section depends on debug_str section. */ - private static final String TARGET_SECTION_NAME = DW_STR_SECTION_NAME; + private static final String TARGET_SECTION_NAME = DwarfDebugInfo.DW_STR_SECTION_NAME; @Override public String targetSectionName() { 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 79299cd40f24..30ef21713a4f 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 @@ -42,9 +42,6 @@ import java.util.Set; import java.util.stream.Stream; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.HEAP_BEGIN_NAME; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.TEXT_SECTION_NAME; - /** * A class from which all DWARF debug sections inherit providing common behaviours. */ @@ -207,7 +204,7 @@ protected int putRelocatableCodeOffset(long l, byte[] buffer, int p) { /* * Mark address so it is relocated relative to the start of the text segment. */ - markRelocationSite(pos, ObjectFile.RelocationKind.DIRECT_8, TEXT_SECTION_NAME, false, Long.valueOf(l)); + markRelocationSite(pos, ObjectFile.RelocationKind.DIRECT_8, DwarfDebugInfo.TEXT_SECTION_NAME, false, Long.valueOf(l)); pos = putLong(0, buffer, pos); return pos; } @@ -217,7 +214,7 @@ protected int putRelocatableHeapOffset(long l, byte[] buffer, int p) { /* * Mark address so it is relocated relative to the start of the heap. */ - markRelocationSite(pos, ObjectFile.RelocationKind.DIRECT_8, HEAP_BEGIN_NAME, false, Long.valueOf(l)); + markRelocationSite(pos, ObjectFile.RelocationKind.DIRECT_8, DwarfDebugInfo.HEAP_BEGIN_NAME, false, Long.valueOf(l)); pos = putLong(0, buffer, pos); return pos; } @@ -430,15 +427,15 @@ public Set getDependencies(Map typeInfoProvider() { Stream headerTypeInfo = computeHeaderTypeInfo(); @@ -152,45 +175,31 @@ public Stream dataInfoProvider() { protected static ResolvedJavaType getJavaType(HostedType hostedType, boolean wantOriginal) { ResolvedJavaType javaType; if (wantOriginal) { - // check for wholesale replacement of the original class + /* Check for wholesale replacement of the original class. */ javaType = hostedType.getWrapped().getWrappedWithoutResolve(); - if (javaType instanceof SubstitutionType) { - return ((SubstitutionType) javaType).getOriginal(); - } else if (javaType instanceof CustomSubstitutionType) { - return ((CustomSubstitutionType) javaType).getOriginal(); - } else if (javaType instanceof LambdaSubstitutionType) { - return ((LambdaSubstitutionType) javaType).getOriginal(); - } else if (javaType instanceof InjectedFieldsType) { - return ((InjectedFieldsType) javaType).getOriginal(); - } else { - return javaType; - } + ResolvedJavaType originalType = getOriginal(javaType); + return (originalType != null ? originalType : javaType); } return hostedType.getWrapped().getWrapped(); } protected static ResolvedJavaType getJavaType(HostedMethod hostedMethod, boolean wantOriginal) { if (wantOriginal) { - // check for wholesale replacement of the original class + /* Check for wholesale replacement of the original class. */ HostedType hostedType = hostedMethod.getDeclaringClass(); ResolvedJavaType javaType = hostedType.getWrapped().getWrappedWithoutResolve(); - if (javaType instanceof SubstitutionType) { - return ((SubstitutionType) javaType).getOriginal(); - } else if (javaType instanceof CustomSubstitutionType) { - return ((CustomSubstitutionType) javaType).getOriginal(); - } else if (javaType instanceof LambdaSubstitutionType) { - return ((LambdaSubstitutionType) javaType).getOriginal(); - } else if (javaType instanceof InjectedFieldsType) { - return ((InjectedFieldsType) javaType).getOriginal(); + ResolvedJavaType originalType = getOriginal(javaType); + if (originalType != null) { + return originalType; } - // check for replacement of the original method only + /* Check for replacement of the original method only. */ ResolvedJavaMethod javaMethod = hostedMethod.getWrapped().getWrapped(); if (javaMethod instanceof SubstitutionMethod) { - javaMethod = ((SubstitutionMethod) javaMethod).getOriginal(); + return ((SubstitutionMethod) javaMethod).getOriginal().getDeclaringClass(); } else if (javaMethod instanceof CustomSubstitutionMethod) { - javaMethod = ((CustomSubstitutionMethod) javaMethod).getOriginal(); + return ((CustomSubstitutionMethod) javaMethod).getOriginal().getDeclaringClass(); } - return javaMethod.getDeclaringClass(); + return hostedType.getWrapped().getWrapped(); } ResolvedJavaMethod javaMethod = hostedMethod.getWrapped().getWrapped(); return javaMethod.getDeclaringClass(); @@ -198,29 +207,37 @@ protected static ResolvedJavaType getJavaType(HostedMethod hostedMethod, boolean protected static ResolvedJavaType getJavaType(HostedField hostedField, boolean wantOriginal) { if (wantOriginal) { - // check for wholesale replacement of the original class + /* Check for wholesale replacement of the original class. */ HostedType hostedType = hostedField.getDeclaringClass(); ResolvedJavaType javaType = hostedType.getWrapped().getWrappedWithoutResolve(); - if (javaType instanceof SubstitutionType) { - return ((SubstitutionType) javaType).getOriginal(); - } else if (javaType instanceof CustomSubstitutionType) { - return ((CustomSubstitutionType) javaType).getOriginal(); - } else if (javaType instanceof LambdaSubstitutionType) { - return ((LambdaSubstitutionType) javaType).getOriginal(); - } else if (javaType instanceof InjectedFieldsType) { - return ((InjectedFieldsType) javaType).getOriginal(); + ResolvedJavaType originalType = getOriginal(javaType); + if (originalType != null) { + return originalType; } - // check for replacement of the original field only + /* Check for replacement of the original field only. */ ResolvedJavaField javaField = hostedField.wrapped.wrapped; if (javaField instanceof SubstitutionField) { - javaField = ((SubstitutionField) javaField).getOriginal(); + return ((SubstitutionField) javaField).getOriginal().getDeclaringClass(); } - return javaField.getDeclaringClass(); + return hostedType.getWrapped().getWrapped(); } ResolvedJavaField javaField = hostedField.wrapped.wrapped; return javaField.getDeclaringClass(); } + private static ResolvedJavaType getOriginal(ResolvedJavaType javaType) { + if (javaType instanceof SubstitutionType) { + return ((SubstitutionType) javaType).getOriginal(); + } else if (javaType instanceof CustomSubstitutionType) { + return ((CustomSubstitutionType) javaType).getOriginal(); + } else if (javaType instanceof LambdaSubstitutionType) { + return ((LambdaSubstitutionType) javaType).getOriginal(); + } else if (javaType instanceof InjectedFieldsType) { + return ((InjectedFieldsType) javaType).getOriginal(); + } + return null; + } + private static final Path cachePath = SubstrateOptions.getDebugInfoSourceCacheRoot(); private abstract class NativeImageDebugFileInfo implements DebugFileInfo { @@ -229,12 +246,7 @@ private abstract class NativeImageDebugFileInfo implements DebugFileInfo { @SuppressWarnings("try") NativeImageDebugFileInfo(HostedType hostedType) { ResolvedJavaType javaType = getJavaType(hostedType, false); - Class clazz; - if (hostedType instanceof OriginalClassProvider) { - clazz = ((OriginalClassProvider) hostedType).getJavaClass(); - } else { - clazz = null; - } + Class clazz = hostedType.getJavaClass(); SourceManager sourceManager = ImageSingletons.lookup(SourceManager.class); try (DebugContext.Scope s = debugContext.scope("DebugFileInfo", hostedType)) { fullFilePath = sourceManager.findAndCacheSource(javaType, clazz, debugContext); @@ -247,12 +259,7 @@ private abstract class NativeImageDebugFileInfo implements DebugFileInfo { NativeImageDebugFileInfo(HostedMethod hostedMethod) { ResolvedJavaType javaType = getJavaType(hostedMethod, false); HostedType hostedType = hostedMethod.getDeclaringClass(); - Class clazz; - if (hostedType instanceof OriginalClassProvider) { - clazz = ((OriginalClassProvider) hostedType).getJavaClass(); - } else { - clazz = null; - } + Class clazz = hostedType.getJavaClass(); SourceManager sourceManager = ImageSingletons.lookup(SourceManager.class); try (DebugContext.Scope s = debugContext.scope("DebugFileInfo", hostedType)) { fullFilePath = sourceManager.findAndCacheSource(javaType, clazz, debugContext); @@ -265,12 +272,7 @@ private abstract class NativeImageDebugFileInfo implements DebugFileInfo { NativeImageDebugFileInfo(HostedField hostedField) { ResolvedJavaType javaType = getJavaType(hostedField, false); HostedType hostedType = hostedField.getDeclaringClass(); - Class clazz; - if (hostedType instanceof OriginalClassProvider) { - clazz = ((OriginalClassProvider) hostedType).getJavaClass(); - } else { - clazz = null; - } + Class clazz = hostedType.getJavaClass(); SourceManager sourceManager = ImageSingletons.lookup(SourceManager.class); try (DebugContext.Scope s = debugContext.scope("DebugFileInfo", hostedType)) { fullFilePath = sourceManager.findAndCacheSource(javaType, clazz, debugContext); @@ -323,7 +325,7 @@ public void debugContext(Consumer action) { } } - public String toJavaName(HostedType hostedType) { + public String toJavaName(@SuppressWarnings("hiding") HostedType hostedType) { return getJavaType(hostedType, true).toJavaName(); } @@ -335,16 +337,16 @@ public String typeName() { @Override public int size() { if (hostedType instanceof HostedInstanceClass) { - // We know the actual instance size in bytes. + /* We know the actual instance size in bytes. */ return ((HostedInstanceClass) hostedType).getInstanceSize(); } else if (hostedType instanceof HostedArrayClass) { - // Use the size of header common to all arrays of this type. + /* Use the size of header common to all arrays of this type. */ return OBJECTLAYOUT.getArrayBaseOffset(hostedType.getComponentType().getStorageKind()); } else if (hostedType instanceof HostedInterface) { - // Use the size of the header common to all implementors. + /* Use the size of the header common to all implementors. */ return OBJECTLAYOUT.getFirstFieldOffset(); } else { - // Use the number of bytes needed needed to store the value. + /* Use the number of bytes needed needed to store the value. */ assert hostedType instanceof HostedPrimitiveType; JavaKind javaKind = hostedType.getStorageKind(); return (javaKind == JavaKind.Void ? 0 : javaKind.getByteCount()); @@ -355,17 +357,15 @@ public int size() { private class NativeImageHeaderTypeInfo implements DebugHeaderTypeInfo { String typeName; int size; - JavaKind elementKind; List fieldInfos; - NativeImageHeaderTypeInfo(String typeName, int size, JavaKind baseKind) { + NativeImageHeaderTypeInfo(String typeName, int size) { this.typeName = typeName; this.size = size; - this.elementKind = baseKind; this.fieldInfos = new LinkedList<>(); } - void addField(String name, String valueType, int offset, int size) { + void addField(String name, String valueType, int offset, @SuppressWarnings("hiding") int size) { NativeImageDebugHeaderFieldInfo fieldinfo = new NativeImageDebugHeaderFieldInfo(name, typeName, valueType, offset, size); fieldInfos.add(fieldinfo); } @@ -487,38 +487,41 @@ private Stream computeHeaderTypeInfo() { String hubTypeName = "java.lang.Class"; int arrayLengthOffset = OBJECTLAYOUT.getArrayLengthOffset(); int arrayLengthSize = OBJECTLAYOUT.sizeInBytes(JavaKind.Int); - int arrayIdHashOffset = OBJECTLAYOUT.getArrayIdentityHashcodeOffset(); - int arrayIdHashSize = OBJECTLAYOUT.sizeInBytes(JavaKind.Int); + int idHashOffset = OBJECTLAYOUT.getIdentityHashCodeOffset(); + int idHashSize = OBJECTLAYOUT.sizeInBytes(JavaKind.Int); int objHeaderSize = OBJECTLAYOUT.getFirstFieldOffset(); - // we need array headers for all Java kinds - NativeImageHeaderTypeInfo objHeader = new NativeImageHeaderTypeInfo("_objhdr", objHeaderSize, null); + /* We need array headers for all Java kinds */ + + NativeImageHeaderTypeInfo objHeader = new NativeImageHeaderTypeInfo("_objhdr", objHeaderSize); objHeader.addField("hub", hubTypeName, hubOffset, hubFieldSize); + if (idHashOffset > 0) { + objHeader.addField("idHash", "int", idHashOffset, idHashSize); + } infos.add(objHeader); - // create a header for each + /* Create a header for each array type. */ for (JavaKind arrayKind : ARRAY_KINDS) { String name = "_arrhdr" + arrayKind.getTypeChar(); int headerSize = OBJECTLAYOUT.getArrayBaseOffset(arrayKind); - NativeImageHeaderTypeInfo arrHeader = new NativeImageHeaderTypeInfo(name, headerSize, arrayKind); + NativeImageHeaderTypeInfo arrHeader = new NativeImageHeaderTypeInfo(name, headerSize); arrHeader.addField("hub", hubTypeName, hubOffset, hubFieldSize); - arrHeader.addField("len", "int", arrayLengthOffset, arrayLengthSize); - if (arrayIdHashOffset > 0) { - arrHeader.addField("idHash", "int", arrayIdHashOffset, arrayIdHashSize); + if (idHashOffset > 0) { + arrHeader.addField("idHash", "int", idHashOffset, idHashSize); } + arrHeader.addField("len", "int", arrayLengthOffset, arrayLengthSize); infos.add(arrHeader); } return infos.stream(); } private class NativeImageDebugEnumTypeInfo extends NativeImageDebugInstanceTypeInfo implements DebugEnumTypeInfo { - HostedInstanceClass enumClass; NativeImageDebugEnumTypeInfo(HostedInstanceClass enumClass) { super(enumClass); - this.enumClass = enumClass; } + @Override public DebugTypeKind typeKind() { return DebugTypeKind.ENUM; } @@ -529,6 +532,7 @@ private class NativeImageDebugInstanceTypeInfo extends NativeImageDebugTypeInfo super(hostedType); } + @Override public DebugTypeKind typeKind() { return DebugTypeKind.INSTANCE; } @@ -570,7 +574,6 @@ public String superName() { @Override public Stream interfaces() { - final NativeImageDebugTypeInfo typeInfo = this; return Arrays.stream(hostedType.getInterfaces()).map(this::toJavaName); } @@ -613,8 +616,10 @@ public String valueType() { @Override public int offset() { int offset = field.getLocation(); - // for static fields we need to add in the appropriate partition base - // but only if we have a real offset + /* + * For static fields we need to add in the appropriate partition base but only if we + * have a real offset + */ if (isStatic() && offset >= 0) { if (isPrimitive()) { offset += primitiveStartOffset; @@ -656,11 +661,10 @@ protected class NativeImageDebugMethodInfo extends NativeImageDebugFileInfo impl public String name() { String name = hostedMethod.format("%n"); if ("".equals(name)) { - ResolvedJavaMethod unwrapped = hostedMethod.getWrapped().getWrapped(); - if (unwrapped instanceof SubstitutionMethod) { - unwrapped = ((SubstitutionMethod) unwrapped).getOriginal(); + name = getJavaType(hostedMethod, true).toJavaName(); + if (name.indexOf('.') >= 0) { + name = name.substring(name.lastIndexOf('.') + 1); } - name = unwrapped.format("%h"); if (name.indexOf('$') >= 0) { name = name.substring(name.lastIndexOf('$') + 1); } @@ -690,7 +694,7 @@ public List paramTypes() { @Override public List paramNames() { - // can only provide blank names for now + /* Can only provide blank names for now. */ LinkedList paramNames = new LinkedList<>(); Signature signature = hostedMethod.getSignature(); for (int i = 0; i < signature.getParameterCount(false); i++) { @@ -707,13 +711,12 @@ public int modifiers() { } private class NativeImageDebugInterfaceTypeInfo extends NativeImageDebugInstanceTypeInfo implements DebugInterfaceTypeInfo { - HostedInterface interfaceClass; NativeImageDebugInterfaceTypeInfo(HostedInterface interfaceClass) { super(interfaceClass); - this.interfaceClass = interfaceClass; } + @Override public DebugTypeKind typeKind() { return DebugTypeKind.INTERFACE; } @@ -727,6 +730,7 @@ private class NativeImageDebugArrayTypeInfo extends NativeImageDebugTypeInfo imp this.arrayClass = arrayClass; } + @Override public DebugTypeKind typeKind() { return DebugTypeKind.ARRAY; } @@ -756,6 +760,7 @@ private class NativeImageDebugPrimitiveTypeInfo extends NativeImageDebugTypeInfo this.primitiveType = primitiveType; } + @Override public DebugTypeKind typeKind() { return DebugTypeKind.PRIMITIVE; } @@ -800,7 +805,7 @@ private NativeImageDebugTypeInfo createDebugTypeInfo(HostedType hostedType) { if (hostedType.isEnum()) { return new NativeImageDebugEnumTypeInfo((HostedInstanceClass) hostedType); } else if (hostedType.isInstanceClass()) { - return new NativeImageDebugInstanceTypeInfo((HostedInstanceClass) hostedType); + return new NativeImageDebugInstanceTypeInfo(hostedType); } else if (hostedType.isInterface()) { return new NativeImageDebugInterfaceTypeInfo((HostedInterface) hostedType); } else if (hostedType.isArray()) { @@ -820,7 +825,6 @@ private class NativeImageDebugCodeInfo extends NativeImageDebugFileInfo implemen private final HostedMethod hostedMethod; private final CompilationResult compilation; - @SuppressWarnings("try") NativeImageDebugCodeInfo(HostedMethod method, CompilationResult compilation) { super(method); this.hostedMethod = method; @@ -852,7 +856,10 @@ public String methodName() { } String name = targetMethod.getName(); if (name.equals("")) { - name = targetMethod.format("%h"); + name = getJavaType(hostedMethod, true).toJavaName(); + if (name.indexOf('.') >= 0) { + name = name.substring(name.lastIndexOf('.') + 1); + } if (name.indexOf('$') >= 0) { name = name.substring(name.lastIndexOf('$') + 1); } @@ -865,6 +872,7 @@ public String symbolNameForMethod() { return NativeBootImage.localSymbolNameForMethod(hostedMethod); } + @Override public String paramSignature() { return hostedMethod.format("%P"); } @@ -910,7 +918,7 @@ public int getFrameSize() { public List getFrameSizeChanges() { List frameSizeChanges = new LinkedList<>(); for (CompilationResult.CodeMark mark : compilation.getMarks()) { - // we only need to observe stack increment or decrement points + /* We only need to observe stack increment or decrement points. */ if (mark.id.equals(SubstrateBackend.SubstrateMarkId.PROLOGUE_DECD_RSP)) { NativeImageDebugFrameSizeChange sizeChange = new NativeImageDebugFrameSizeChange(mark.pcOffset, EXTEND); frameSizeChanges.add(sizeChange); @@ -922,7 +930,7 @@ public List getFrameSizeChanges() { NativeImageDebugFrameSizeChange sizeChange = new NativeImageDebugFrameSizeChange(mark.pcOffset, CONTRACT); frameSizeChanges.add(sizeChange); } else if (mark.id.equals(SubstrateBackend.SubstrateMarkId.EPILOGUE_END) && mark.pcOffset < compilation.getTargetCodeSize()) { - // there is code after this return point so notify stack extend again + /* There is code after this return point so notify a stack extend again. */ NativeImageDebugFrameSizeChange sizeChange = new NativeImageDebugFrameSizeChange(mark.pcOffset, EXTEND); frameSizeChanges.add(sizeChange); } @@ -935,6 +943,7 @@ public boolean isDeoptTarget() { return methodName().endsWith(HostedMethod.METHOD_NAME_DEOPT_SUFFIX); } + @Override public int getModifiers() { return hostedMethod.getModifiers(); } @@ -995,11 +1004,8 @@ public String className() { @Override public String methodName() { ResolvedJavaMethod targetMethod = method; - if (targetMethod instanceof HostedMethod) { - targetMethod = ((HostedMethod) targetMethod).getWrapped(); - } - if (targetMethod instanceof AnalysisMethod) { - targetMethod = ((AnalysisMethod) targetMethod).getWrapped(); + while (targetMethod instanceof WrappedJavaMethod) { + targetMethod = ((WrappedJavaMethod) targetMethod).getWrapped(); } if (targetMethod instanceof SubstitutionMethod) { targetMethod = ((SubstitutionMethod) targetMethod).getOriginal(); @@ -1008,9 +1014,19 @@ public String methodName() { } String name = targetMethod.getName(); if (name.equals("")) { - name = targetMethod.format("%h"); - if (name.indexOf('$') >= 0) { - name = name.substring(name.lastIndexOf('$') + 1); + if (method instanceof HostedMethod) { + name = getJavaType((HostedMethod) method, true).toJavaName(); + if (name.indexOf('.') >= 0) { + name = name.substring(name.lastIndexOf('.') + 1); + } + if (name.indexOf('$') >= 0) { + name = name.substring(name.lastIndexOf('$') + 1); + } + } else { + name = targetMethod.format("%h"); + if (name.indexOf('$') >= 0) { + name = name.substring(name.lastIndexOf('$') + 1); + } } } return name; @@ -1098,8 +1114,6 @@ private class NativeImageDebugDataInfo implements DebugDataInfo { long offset; long address; long size; - ResolvedJavaType javaType; - Class clazz; String typeName; String provenance; @@ -1120,48 +1134,43 @@ public void debugContext(Consumer action) { address = objectInfo.getAddress(); size = objectInfo.getSize(); provenance = objectInfo.toString(); - /* - * HostedType wraps an AnalysisType and both HostedType and AnalysisType punt calls to - * getSourceFilename to the wrapped class so for consistency we need to do the type name - * and path lookup relative to the doubly unwrapped HostedType. - */ - javaType = hostedClass.getWrapped().getWrappedWithoutResolve(); - if (hostedClass instanceof OriginalClassProvider) { - clazz = ((OriginalClassProvider) hostedClass).getJavaClass(); - } else { - clazz = null; - } typeName = hostedClass.toJavaName(); } - // accessors + /* Accessors. */ + @Override public String getProvenance() { return provenance; } + @Override public String getTypeName() { return typeName; } + @Override public String getPartition() { return partition.getName() + "{" + partition.getSize() + "}@" + partition.getStartOffset(); } + @Override public long getOffset() { return offset; } + @Override public long getAddress() { return address; } + @Override public long getSize() { return size; } } private boolean acceptObjectInfo(ObjectInfo objectInfo) { - // this rejects filler partition objects + /* This condiiton rejects filler partition objects. */ return (objectInfo.getPartition().getStartOffset() > 0); } From 9757c9c94e78615d039ded8ee87bc3ab2c0351ce Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Thu, 4 Feb 2021 09:37:43 +0000 Subject: [PATCH 4/6] Fix conversion of indirect to war oops using wrapper subclasses --- substratevm/DebugInfo.md | 263 ++++++---- .../objectfile/debugentry/DebugInfoBase.java | 99 +++- .../debugentry/InterfaceClassEntry.java | 20 + .../debuginfo/DebugInfoProvider.java | 20 +- .../elf/dwarf/DwarfAbbrevSectionImpl.java | 298 +++++++++--- .../objectfile/elf/dwarf/DwarfDebugInfo.java | 113 ++++- .../elf/dwarf/DwarfInfoSectionImpl.java | 460 ++++++++++++++---- .../elf/dwarf/DwarfSectionImpl.java | 22 + .../image/NativeImageDebugInfoProvider.java | 34 +- 9 files changed, 1008 insertions(+), 321 deletions(-) diff --git a/substratevm/DebugInfo.md b/substratevm/DebugInfo.md index e94195177cee..cc0185398cf6 100644 --- a/substratevm/DebugInfo.md +++ b/substratevm/DebugInfo.md @@ -86,78 +86,6 @@ _apps/target/hello.jar_ and _apps/target/greeter.jar_ will be used to derive the default search roots _apps/target/hello-sources.jar_ and _apps/target/greeter-sources.jar_. -## Debugging with Isolates - -Note that it is currently recommended to disable use of Isolates by -passing flag `-H:-UseIsolates` on the command line when debug info -generation is enabled. Enabling of Isolates affects the way that oops -(object references) are encoded. In turn that means the debug info -generator has to provide gdb with information about how to translate -an encoded oop to the address in memory where the object data is -stored. This sometimes requires care when asking gdb to process -encoded oops vs decoded raw addresses. - -When isolates are disabled oops are essentially raw addresses pointing -directly at the object contents. This is the same whether the oop is -stored in a static/instance field or has been loaded into a register. - -When an oop is stored in a static or instance field gdb knows the type -of the value stored in the field and knows how to dereference it to -locate the object contents. For example, assume we have a `Units` type -that details the scale used for a blueprint drawing and that the -`Units` instance has a `String` field called `print_name`. Assume also -we have a static field `DEFAULT_UNIT` that holds the standard `Units` -instance. The following command will print the name for the default -units. - -``` -(gdb) print *com.acme.Blueprint::DEFAULT_UNIT->print_name -``` - -gdb knows the type of the oop stored in `Blueprint::DEFAULT_UNIT` and -knows how to dereference it to locate the object field -values. Likewise, it knows that the `print_name` field is a `String` -it will translate the oop stored in that field to an address where the -String contents are stored and it will print the values of the -`String` instance fields one by one. - -If, say, an oop referring to the `print_name` String has been loaded -into $rdx it is still possible to print it using a straightforward -cast to the pointer type that gdb associates with oop references. - -``` -(gdb) print/x *('java.lang.String' *)$rdx -``` - -The raw address in the register is the same as the oop value stored -in the field. - -By contrast, when isolates are enabled oop references stored in static -or instance fields are actually relative addresses, offsets from a -dedicated heap base register (r14 on x86_64, r29 on AArch64), rather -than direct addresses. However, when an oop gets loaded during -execution it is almost always immediately converted to a direct -address by adding the offset to the heap base register value. The -DWARF info encoded into the image tells gdb to rebase object pointers -whenever it tries to dereference them to access the underlying object -data. - -This still means gdb will do the right thing when it accesses an -object via a static field. When processing the field expression above -that prints the default unit name gdb will automatically rebase the -`Units` oop stored in field `DEFAULT_UNIT` by adding it to the heap -base register. It will then fetch and rebase the oop stored in its -`print_name` field to access the contents of the `String`. - -However, this transformation won't work correctly in the second case -where gdb is passed an oop that has already been loaded into a -register and converted to a pointer. It is necessary to restore the -original oop by reverting it back to an offset: - -``` -(gdb) print/x *('java.lang.String' *)($rdx - $r14) -``` - ## Currently Implemented Features The currently implemented features include: @@ -251,11 +179,21 @@ The hub field in the object header is actually a reference of Java type `java.lang.Class`. Note that the field is typed by gdb using a pointer to the underlying C++ class (layout) type. +All classes, from Object downwards inherit from a common, automatically +generated header type _objhdr. It is this header type which includes +the hub field: + ``` (gdb) ptype _objhdr type = struct _objhdr { java.lang.Class *hub; } + +(gdb) ptype 'java.lang.Object' +type = class java.lang.Object : public _objhdr { + public: + void Object(void); + . . . ``` Given an address that might be an object reference it is possible to @@ -288,7 +226,7 @@ Casting it to this type shows it has length 1. $4 = { <_arrhdrA> = { hub = 0x906a78, - len = 2, + len = 1, idHash = 0 }, members of java.lang.String[]: @@ -304,32 +242,29 @@ printed is as follows: 798: "[Ljava.lang.String;" ``` -Indeed it is useful to define a gdb command `hubname` to execute this -operation on an arbitrary input argument +Indeed it is useful to define a gdb command `hubname_raw` to execute this +operation on an arbitrary raw memory address ``` -command hubname +command hubname_raw x/s ((_objhdr *)($arg0))->hub->name->value->data end -(gdb) hubname $2 +(gdb) hubname_raw $2 0x904798: "[Ljava.lang.String;" ``` -Notice that the `hubname` command also masks out the low 3 flag bits in -the hub field that may sometimes get set by the runtime during program operation. - Attempting to print the hub name for an invalid reference will fail safe, printing an error message. ``` -(gdb) p/x (_objhdr *)$rdx +(gdb) p/x $rdx $5 = 0x2 (gdb) hubname $rdx Cannot access memory at address 0x2 ``` -Array type layouts are modelled with a class. The class inherits +Array type layouts are also modelled with a class. The class inherits fields from an array header struct specific to the array element type, one of _arrhdrZ _arrhdrB, _arrhdrS, ... _arrhdrA (the last one is for object arrays). Inherited fields include the hub, array length, idHash @@ -347,7 +282,27 @@ type = struct java.lang.String[] : public _arrhdrA { Notice that the type of the values stored in the data array is `java.lang.String *` i.e. the C++ array stores Java references -i.e. addresss as far as the C++ model is concerned. +i.e. addresses as far as the C++ model is concerned. + +When gdb knows the Java type for a reference it can be printed without +casting using a simpler version of the hubname command. For example, +the String array retrieved above as $4 has a known type. + +``` +(gdb) ptype $4 +type = struct java.lang.String[] : public _arrhdrA { + java.lang.String *data[0]; +} + +command hubname + x/s (($arg0))->hub->name->value->data +end + +(gdb) hubname $4 +0x923b68: "[Ljava.lang.String;" +``` + +(notice that gdb automatically uses the address of the printed object) The array header structs are all extensions of the basic _objhdr type which means that arrays and objects can both be safely cast to oops. @@ -382,17 +337,25 @@ If we take the first String in the args array we can ask gdb to cast it to interface CharSequence ``` (gdb) print (('java.lang.String[]' *)$rdi)->data[0] -$6 = (java.lang.String) 0x7ffff7c01060 -(gdb) print ('java.lang.CharSequence')$6 -$7 = (java.lang.CharSequence) 0x7ffff7c01060 +$6 = (java.lang.String *) 0x7ffff7c01060 +(gdb) print ('java.lang.CharSequence' *)$6 +$7 = (java.lang.CharSequence *) 0x7ffff7c01060 ``` -The hubname command can be used to identify the actual type of the -object that implements this interface and that type name can be used -to select the union element used to print the object. +The hubname command won't work with this union type because it is +only objects of the elements of the union that include the hub field: ``` (gdb) hubname $7 +There is no member named hub. +``` + +However, since all elements include the same header any one of them +can be passed to hubname in order to identify the actual type. This +allows the correct union element to be selected: + +``` +(gdb) hubname $7->'_java.nio.CharBuffer' 0x7d96d8: "java.lang.String\270", (gdb) print $7->'_java.lang.String' $18 = { @@ -407,6 +370,9 @@ $18 = { } ``` +Notice that the printed class name includes some trailing characters. +That's because Java strings are not guaranteed to be zero-terminated. + The current debug info model does not include the location info needed to allow symbolic names for local vars and parameter vars to be resolved to primitive values or object references. However, the @@ -684,3 +650,122 @@ The prototype is currently implemented only for the GNU Debugger on Linux: may be incorrect) Windows support is still under development. + +## Debugging with Isolates + +Note that it is currently recommended to disable use of Isolates by +passing flag `-H:-UseIsolates` on the command line when debug info +generation is enabled. Enabling of Isolates affects the way that oops +(object references) are encoded. In turn that means the debug info +generator has to provide gdb with information about how to translate +an encoded oop to the address in memory where the object data is +stored. This sometimes requires care when asking gdb to process +encoded oops vs decoded raw addresses. + +When isolates are disabled oops are essentially raw addresses pointing +directly at the object contents. This is generally the same whether +the oop is embedded in a static/instance field or is referenced from a +local or parameter variable located in a register or saved to the stack. +It's not quite that simple because the bottom 3 bits of some oops may +be used to hold "tags" that record certain transient properties of +an object. However, the debuginfo provided to gdb means that it will +remove these tag bits before dereferencing the oop as an address. + +By contrast, when isolates are enabled oop references stored in static +or instance fields are actually relative addresses, offsets from a +dedicated heap base register (r14 on x86_64, r29 on AArch64), rather +than direct addresses (in a few special cases the offset may also have +some low tag bits set). When an 'indirect' oop of this kind gets loaded +during execution it is almost always immediately converted to a 'raw' +address by adding the offset to the heap base register value. So, oops +which occur as the value of local or parameter vars are actually raw +addresses. + +The DWARF info encoded into the image when isolates are enabled tells +gdb to rebase indirect oops whenever it tries to dereference them to +access underlying object data. This is normally automatic and +transparent but it is visible in the underlying type model that gdb +displays when you ask for the type of objects. + +For example, consider the static field we encountered above. Printing +its type in an image that uses Isolates shows that this (static) field +has a different type to the expected one: + +``` +(gdb) ptype 'java.math.BigInteger'::powerCache +type = class _z_.java.math.BigInteger[][] : public java.math.BigInteger[][] { +} * +``` +The field is typed as '_z_.java.math.BigInteger[][]' which is an empty +wrapper class that inherits from the expected type +'java.math.BigInteger[][]'. This wrapper type is essentially the same +as the original but the DWARFINFO that defines it includes information +that tells gdb how to convert pointers to this type. + +If gdb is asked to print the oop stored in this field it is clear that +it is an offset rather than a raw address. + +``` +(gdb) p/x 'java.math.BigInteger'::powerCache +$1 = 0x286c08 +(gdb) x/x 0x286c08 +0x286c08: Cannot access memory at address 0x286c08 +``` + +However, when gdb is asked to dereference through the field it applies +the necessary address conversion to the oop and fetches the correct +data. + +``` +(gdb) p/x *'java.math.BigInteger'::powerCache +$2 = { + = { + <_arrhdrA> = { + hub = 0x1ec0e2, + idHash = 0x76381891, + len = 0x25 + }, + members of java.math.BigInteger[][]: + data = 0x7ffff7a86c18 + }, } +``` + +Printing the type of the hub field or the data array shows that they +are also modelled using indirect types: + +``` +(gdb) ptype $1->hub +type = class _z_.java.lang.Class : public java.lang.Class { +} * +(gdb) ptype $2->data +type = class _z_.java.math.BigInteger[] : public java.math.BigInteger[] { +} *[0] +``` + +gdb still knows how to dereference these oops: + +``` +(gdb) p $1->hub +$3 = (_z_.java.lang.Class *) 0x1ec0e2 +(gdb) x/x $1->hub +0x1ec0e2: Cannot access memory at address 0x1ec0e2 +(gdb) p *$1->hub +$4 = { + = { + = { + <_objhdr> = { + hub = 0x1dc860, + idHash = 550750288 + }, }, + members of java.lang.Class: + name = 0x171af8, + . . . + }, } + +``` + +Since the indirect types inherit from the corresponding raw type it is +possible to use an expression that identifies an indirect type pointer +in almost all cases where an expression identifying a raw type pointer +would work. The only case case where care might be needed is when +casting a displayed numeric field value or displayed register value. \ No newline at end of file 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 6c8910e1ccef..6aad9bd27c5e 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 @@ -28,6 +28,7 @@ import com.oracle.objectfile.debuginfo.DebugInfoProvider; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugTypeInfo.DebugTypeKind; +import com.oracle.objectfile.elf.dwarf.DwarfDebugInfo; import org.graalvm.compiler.debug.DebugContext; import java.nio.ByteOrder; @@ -113,16 +114,35 @@ public abstract class DebugInfoBase { * otherwise false. */ private boolean useHeapBase; - private int oopShiftBitCount; - private int oopFlagBitsMask; - private int oopReferenceByteCount; + /** + * number of bits oops are left shifted by when using compressed oops + */ + private int oopCompressShift; + /** + * number of low order bits used for tagging oops + */ + private int oopTagsCount; + /** + * number of bytes used to store an oop reference + */ + private int oopReferenceSize; + /** + * alignment of object memory area (and, therefore, of any oop) in bytes + */ + private int oopAlignment; + /** + * number of bits in oop which are guaranteed 0 by virtue of alignment + */ + private int oopAlignShift; public DebugInfoBase(ByteOrder byteOrder) { this.byteOrder = byteOrder; this.useHeapBase = true; - this.oopFlagBitsMask = 0; - this.oopShiftBitCount = 0; - this.oopReferenceByteCount = 0; + this.oopTagsCount = 0; + this.oopCompressShift = 0; + this.oopReferenceSize = 0; + this.oopAlignment = 0; + this.oopAlignShift = 0; } /** @@ -145,19 +165,34 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { useHeapBase = debugInfoProvider.useHeapBase(); /* - * save mask for low order flag bits + * save count of low order tag bits that may appear in references */ - oopFlagBitsMask = debugInfoProvider.oopFlagBitsMask(); - /* flag bits be bewteen 1 and 32 for us to emit as DW_OP_lit */ - assert oopFlagBitsMask > 0 && oopFlagBitsMask < 32; + int oopTagsMask = debugInfoProvider.oopTagsMask(); + + /* tag bits must be between 1 and 32 for us to emit as DW_OP_lit */ + assert oopTagsMask > 0 && oopTagsMask < 32; /* mask must be contiguous from bit 0 */ - assert ((oopFlagBitsMask + 1) & oopFlagBitsMask) == 0; + assert ((oopTagsMask + 1) & oopTagsMask) == 0; + + oopTagsCount = Integer.bitCount(oopTagsMask); /* Save amount we need to shift references by when loading from an object field. */ - oopShiftBitCount = debugInfoProvider.oopShiftBitCount(); + oopCompressShift = debugInfoProvider.oopCompressShift(); + + /* shift bit count must be either 0 or 3 */ + assert (oopCompressShift == 0 || oopCompressShift == 3); /* Save number of bytes in a reference field. */ - oopReferenceByteCount = debugInfoProvider.oopReferenceByteCount(); + oopReferenceSize = debugInfoProvider.oopReferenceSize(); + + /* Save alignment of a reference */ + oopAlignment = debugInfoProvider.oopAlignment(); + + /* Save alignment of a reference */ + oopAlignShift = Integer.bitCount(oopAlignment - 1); + + /* reference alignment must be 8 bytes */ + assert oopAlignment == 8; /* Ensure we have a null string in the string section. */ stringTable.uniqueDebugString(""); @@ -433,15 +468,41 @@ public boolean useHeapBase() { return useHeapBase; } - public byte oopFlagBitsMask() { - return (byte) oopFlagBitsMask; + public byte oopTagsMask() { + return (byte) ((1 << oopTagsCount) - 1); + } + + public byte oopTagsShift() { + return (byte) oopTagsCount; + } + + public int oopCompressShift() { + return oopCompressShift; + } + + public int oopReferenceSize() { + return oopReferenceSize; } - public int oopShiftBitCount() { - return oopShiftBitCount; + public int oopAlignment() { + return oopAlignment; } - public int oopReferenceByteCount() { - return oopReferenceByteCount; + public int oopAlignShift() { + return oopAlignShift; + } + + public boolean isHubClassEntry(ClassEntry classEntry) { + return classEntry.getTypeName().equals(DwarfDebugInfo.HUB_TYPE_NAME); + } + + public int classLayoutAbbrevCode(ClassEntry classEntry) { + if (useHeapBase & isHubClassEntry(classEntry)) { + /* + * this layout adds special logic to remove tag bits from indirect pointers to this type + */ + return DwarfDebugInfo.DW_ABBREV_CODE_class_layout2; + } + return DwarfDebugInfo.DW_ABBREV_CODE_class_layout1; } } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/InterfaceClassEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/InterfaceClassEntry.java index 3b8975a31bba..df204bd0c4dd 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/InterfaceClassEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/InterfaceClassEntry.java @@ -62,4 +62,24 @@ public void addImplementor(ClassEntry classEntry, DebugContext debugContext) { public Stream implementors() { return implementors.stream(); } + + @Override + public int getSize() { + /* + * An interface is nominally sized to the class header size when it is first created. This + * override computes the size of the union layout that models the interface as the maximum + * size of all the type class types that are embedded in that union. This result is used to + * size of the wrapper class that handles address translation for values embedded in object + * fields. + */ + int maxSize = super.size; + for (ClassEntry implementor : implementors) { + int size = implementor.getSize(); + + if (size > maxSize) { + maxSize = size; + } + } + return maxSize; + } } 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 4e24ca6f2aa3..d5e4a8d8120b 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 @@ -40,11 +40,25 @@ public interface DebugInfoProvider { boolean useHeapBase(); - int oopShiftBitCount(); + /** + * number of bits oops are left shifted by when using compressed oops + */ + int oopCompressShift(); - int oopFlagBitsMask(); + /** + * mask delecting low order bits used for tagging oops + */ + int oopTagsMask(); - int oopReferenceByteCount(); + /** + * number of bytes used to store an oop reference + */ + int oopReferenceSize(); + + /** + * alignment of object memory area (and, therefore, of any oop) in bytes + */ + int oopAlignment(); /** * An interface implemented by items that can be located in a file. 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 c323e18c42e8..0c23554929f8 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 @@ -139,6 +139,17 @@ public void createContent() { *
    • code = interface_pointer, tag = pointer_type, parent = class_unit - Java * interface ref type * + *
    • code = indirect_layout, tag = class_type, parent = class_unit, array_unit, + * interface_unit - wrapper layout attaches address rewriting logic to the layout + * types that it wraps using a data_location attribute + * + *
    • code = indirect_pointer, tag = pointer_type, parent = class_unit, array_unit, + * interface_unit - indirect ref type used to type indirect oops that encode the + * address of an object, whether by adding tag bits or representing the address as an offset + * from some base address. these are used to type object references stored in static and + * instance fields. They are not needed when typing local vars and parameters held in + * registers or on the stack as they appear as raw addresses. + * *
    * *
      @@ -154,9 +165,9 @@ public void createContent() { * object_header/class_layout - object header or instance field declaration (i.e. * specification of properties) * - *
    • code == super_reference, tag == inheritance, parent = - * class_layout/array_layout - reference to super class layout or to appropriate - * header struct for {code java.lang.Object} or arrays. + *
    • code == super_reference, tag == inheritance, parent = class_layout, + * array_layout - reference to super class layout or to appropriate header struct for + * {code java.lang.Object} or arrays. * *
    • code == interface_implementor, tag == member, parent = interface_layout * - union member typed using class layout of a given implementing class @@ -213,8 +224,6 @@ public void createContent() { * *
    • DW_AT_byte_size : ... DW_FORM_data1 "oop" * - *
    • DW_AT_location : ........ DW_FORM_expr_loc - * *
    * * Header Data: A level 1 DIE of type member is used to describe the fields of both object @@ -249,11 +258,9 @@ public void createContent() { * *
  • DW_AT_comp_dir : ... DW_FORM_strp * - *
  • DW_AT_low_pc : ..... DW_FORM_address ??? omit this so method entries do - * not need to occupy full range + *
  • DW_AT_low_pc : ..... DW_FORM_address * - *
  • DW_AT_hi_pc : ...... DW_FORM_address ??? omit this so method entries do - * not need to occupy full range + *
  • DW_AT_hi_pc : ...... DW_FORM_address * *
  • DW_AT_stmt_list : .. DW_FORM_data4 n.b only for abbrev-code == * class_unit1 @@ -261,31 +268,35 @@ public void createContent() { * * * Instance Class Structure: Each class_unit DIE contains a series of level 1 DIEs. The - * first one describes the class layout: + * first one describes the class layout. The normal layout does not include a data_location + * attribute. However, an alternative layout, including that extra attribute, is provided to + * ensure that tag bits can be removed from pointers to instances of java.lang.Class. This + * alternative layout is only needed when a heapbase register is not in use and fields hold + * raw oops. If a heapbase register is in use and fields hold indirect oops then the masking + * logic for* class pointer tags is included in the data_location attribute attached to the + * indirect layout record (see below) * *
      * - *
    • abbrev_code == class_layout, tag == DW_TAG_class_type, has_children + *
    • abbrev_code == class_layout1/class_layout2, tag == DW_TAG_class_type, + * has_children * *
    • Dw_AT_name : ........ DW_FORM_strp * - *
    • Dw_AT_byte_size : ... DW_FORM_data1/2 ??? how many bytes do we really - * need? + *
    • Dw_AT_byte_size : ... DW_FORM_data1/2 * - *
    • Dw_AT_decl_file : ... DW_FORM_data1/2 ??? how many bytes do we really - * need? + *
    • Dw_AT_decl_file : ... DW_FORM_data1/2 * - *
    • Dw_AT_decl_line : ... DW_FORM_data1/2 ??? how many bytes do we really - * need? + *
    • Dw_AT_decl_line : ... DW_FORM_data1/2 * - *
    • Dw_AT_data_location : ... DW_FORM_expr_loc + *
    • Dw_AT_data_location : ... DW_FORM_expr_loc n.b. only for class_layout2 * *
    * - * Instance Class members: The level 1 class_layout DIE includes a level 2 child for each of + * Instance Class members: A level 1 class_layout DIE includes a level 2 child for each of * the class's methods and fields. The first type declares a method but omits details of the * location of the code that implements the method. The second type declares an instance or - * static field. The class_layout DIE also contains an level 2 DIE specifying the type from + * static field. A class_layout DIE also contains an level 2 DIE specifying the type from * which it inherits superclass structure. In the case of class Object structure is * inherited from the object header structure type. * @@ -300,13 +311,13 @@ public void createContent() { *
  • abbrev_code == method_declaration1/2, tag == DW_TAG_subprogram, * has_children * - *
  • DW_AT_external : .......... DW_FORM_flag ??? for all? + *
  • DW_AT_external : .......... DW_FORM_flag * *
  • Dw_AT_name : .............. DW_FORM_strp * - *
  • DW_AT_decl_file : ......... DW_FORM_data1/2 ??? how many bytes + *
  • DW_AT_decl_file : ......... DW_FORM_data1/2 * - *
  • DW_AT_decl_line : ......... DW_FORM_data1/2 ??? how many bytes + *
  • DW_AT_decl_line : ......... DW_FORM_data1/2 * *
  • Dw_AT_linkage_name : ...... DW_FORM_strp * @@ -318,8 +329,8 @@ public void createContent() { * *
  • DW_AT_declaration : ....... DW_FORM_flag * - *
  • Dw_AT_object_pointer : .... DW_FORM_ref_addr (only for - * method_declaration1 points to param 0 DIE) + *
  • Dw_AT_object_pointer : .... DW_FORM_ref_addr n.b. only for + * method_declaration1, points to param 0 DIE * *
  • DW_AT_virtuality : ........ DW_FORM_data1 (for override methods) * @@ -334,26 +345,26 @@ public void createContent() { * *
  • Dw_AT_name : ................... DW_FORM_strp * - *
  • DW_AT_decl_file : .............. DW_FORM_data1/2 (only for - * field_declaration2/4) + *
  • DW_AT_decl_file : .............. DW_FORM_data1/2 n.b. only for + * field_declaration2/4 * - *
  • DW_AT_decl_line : .............. DW_FORM_data1/2 (only for - * field_declaration2/4) + *
  • DW_AT_decl_line : .............. DW_FORM_data1/2 n.b. only for + * field_declaration2/4 * *
  • Dw_AT_type : ................... DW_FORM_ref_addr * - *
  • Dw_AT_data_member_location : ... DW_FORM_data1/2 (only for - * field_declaration1/2 instance) ??? how many bytes? + *
  • Dw_AT_data_member_location : ... DW_FORM_data1/2 (n.b. nly for + * field_declaration1/2 instance * - *
  • Dw_AT_artificial : ............. DW_FORM_flag ?? do we need this? + *
  • Dw_AT_artificial : ............. DW_FORM_flag * *
  • Dw_AT_accessibility : .......... DW_FORM_data1 * - *
  • Dw_AT_external : ............... DW_FORM_flag (only for - * field_declaration3/4 static) + *
  • Dw_AT_external : ............... DW_FORM_flag (n.b. only for + * field_declaration3/4 static * - *
  • Dw_AT_declaration : ............ DW_FORM_flag (only for - * field_declaration3/4 static) + *
  • Dw_AT_declaration : ............ DW_FORM_flag n.b. only for + * field_declaration3/4 static * * * @@ -379,22 +390,50 @@ public void createContent() { * *
  • Dw_AT_name : ... DW_FORM_strp (may be empty string) * - *
  • Dw_AT_file : ... DW_FORM_data1/2 (optional only for - * method_parameter_declaration2) + *
  • Dw_AT_file : ... DW_FORM_data1/2 n.b. only for + * method_parameter_declaration2 * - *
  • Dw_AT_line : ... DW_FORM_data1/2 (optional only for - * method_parameter_declaration2) + *
  • Dw_AT_line : ... DW_FORM_data1/2 n.b. only for + * method_parameter_declaration2 * *
  • Dw_AT_type : ... DW_FORM_ref_addr * - *
  • Dw_AT_artificial : ... DW_FORM_flag (optional only for - * method_parameter_declaration1 $this, $access) + *
  • Dw_AT_artificial : ... DW_FORM_flag n.b. only for + * method_parameter_declaration1 used for this and access vars + * + * + * + * Indirect Instance Class Structure: The level 1 class layout DIE may be followed by a + * level 1 indirect_layout DIE that wraps the class layout as a super class. The wrapper + * type supplies a data_location attribute, allowing indirect pointers to the class (see + * next item) to be translated to raw addresses. The name of the indirect type is + * constructed by prefixing the class name with DwarfDebufInfo.INDIRECT_PREFIX. This DIE has + * only one child DIE with type super_reference (see above). The latter references the class + * layout DIE as a super, effectively embedding the standard layout type in the indirect + * layout. The size of the indirect layout is the same as the size of the class layout. + * + *
      + * + *
    • abbrev_code == indirect_layout, tag == DW_TAG_class_type, has_children + * + *
    • Dw_AT_name : ........ DW_FORM_strp + * + *
    • Dw_AT_byte_size : ... DW_FORM_data1/2 + * + *
    • Dw_AT_data_location : ... DW_FORM_expr_loc * *
    * - * Instance Class Reference Types: A level 1 class_layout DIE is followed by a DIE defining - * a pointer to the class. This reflects the fact that a Java object reference is actually - * implemented as a pointer. + * Instance Class Reference Types: The level 1 class_layout and indirect_layout DIEs are + * followed by DIEs defining pointers to the respective class layouts. A class_pointer DIE + * defines a pointer type for the class_layout type and is used to type pointers which + * directly address an instance. It is used to type local and parameter var references + * whether located in a register or on the stack. It is followed by an indirect_pointer DIE + * which defines a pointer type for the class's indirect_layout type. This is used to type + * references to instances of the class located in a static or instance field. These + * references require address translation by masking off tag bits and rebasing from an + * offset to a raw address. The logic for this translation is encoded in a data_location + * attribute of the indirect_layout DIE. * *
      * @@ -406,6 +445,16 @@ public void createContent() { * *
    * + *
      + * + *
    • abbrev_code == indirect_pointer, tag == DW_TAG_pointer_type, no_children + * + *
    • Dw_AT_byte_size : ... DW_FORM_data1 + * + *
    • Dw_AT_type : ........ DW_FORM_ref_addr + * + *
    + * * n.b. the name used in the class_layout DIE is the Java class name. This is deliberately * inconsistent with the Java naming where the name refers to the pointer type. In * consequence when gdb displays Java types and signatures oop reference appear as pointer @@ -446,7 +495,7 @@ public void createContent() { * *
  • DW_AT_low_pc : .......... DW_FORM_addr * - *
  • DW_AT_hi_pc : ........... DW_FORM_addr (or data8???) + *
  • DW_AT_hi_pc : ........... DW_FORM_addr * *
  • DW_AT_external : ........ DW_FORM_flag * @@ -478,12 +527,15 @@ public void createContent() { * *
  • DW_AT_language : ... DW_FORM_data1 * - *
  • DW_AT_name : ....... DW_FORM_strp ??? what name??? + *
  • DW_AT_name : ....... DW_FORM_strp * * * - * Array Structure: Each array_unit DIE contains two level 1 DIEs. The first one describes - * the array layout: + * Array Structure: Each array_unit DIE contains four level 1 DIEs. The first one describes + * the array layout. It has only one child, a super_reference DIE (see above) that + * references the appropriate array header type for an obnject aray or primitive array of + * the relevant primitive type). The size of the array layout is the same as the size of the + * array header. * *
      * @@ -491,15 +543,23 @@ public void createContent() { * *
    • Dw_AT_name : ........ DW_FORM_strp * - *
    • Dw_AT_byte_size : ... DW_FORM_data1/2 size up to base of embedded array - * elements? - * - *
    • DW_AT_location : .... DW_FORM_expr_loc + *
    • Dw_AT_byte_size : ... DW_FORM_data1/2 * *
    * - * The second DIE defines the array reference type as a pointer to the underlying structure - * type + * The immediately following DIE is an indirect_layout (see above) that wraps the array + * layout as its super type (just as with class layouts). The wrapper type supplies a + * data_location attribute, allowing indirect pointers to the array to be translated to raw + * addresses. The name of the indirect array type is constructed by prefixing the array name + * with INDIRECT_PREFIX. This DIE has only one child DIE with type super_reference (see + * above). The latter references the array layout DIE, effectively embedding the standard + * array layout type in the indirect layout. The size of the indirect layout is the same as + * the size of the array layout. + * + * The third and fourth DIEs define array reference types as a pointers to the underlying + * structure layout types. As with classes, there is an array_pointer type for raw address + * references used to type local and param vars and an indirect_pointer type (see above) for + * array references stored in static and instance fields. * *
      * @@ -509,6 +569,8 @@ public void createContent() { * *
    • Dw_AT_type : ........ DW_FORM_ref_addr * + *
    + * * n.b. the name used in the array_layout DIE is the Java array name. This is deliberately * inconsistent with the Java naming where the name refers to the pointer type. As with * normal objects an array reference in a Java signature appears as a pointer to an array @@ -516,7 +578,7 @@ public void createContent() { * * Array members: The level 1 array_layout DIE includes level 2 child DIEs with tag member * that describe the layout of the array. header_field DIEs are used to declare members of - * the array header, including the zero length array data member tat dfollows other header + * the array header, including the zero length array data member that follows other header * fields. An auxiliary array_data_type DIE with tag array_type also occurs as a child DIE * defining the type for the array data member. * @@ -535,13 +597,8 @@ public void createContent() { * * Interface Layout and Reference Types: The level 0 class_unit DIE for an interface is * followed by a level 1 DIE defining the interface layout as a union of all the layouts for - * the classes which implement the interface. A second level 1 DIEs defines a pointer to - * this layout type. - * - * n.b. the name used in the interface_layout DIE is the Java array name. This is - * deliberately inconsistent with the Java naming where the name refers to the pointer type. - * As with normal objects an interface reference in a Java signature appears as a pointer to - * an interface layout when printed by gdb. + * the classes which implement the interface. The size of the interface layout is the + * maximum of the sizes for the implementing classes. * *
      * @@ -553,6 +610,27 @@ public void createContent() { * *
    * + * A second level 1 DIE provides an indirect_layout that wraps the interface layout as its + * super type (just as with class layouts). The wrapper type supplies a data_location + * attribute, allowing indirect pointers to the interface to be translated to raw addresses. + * The name of the indirect interface type is constructed by prefixing the interface name + * with INDIRECT_PREFIX. This DIE has only one child DIE with type super_reference (see + * above). The latter references the interface layout DIE, effectively embedding the + * standard interface layout type in the indirect layout. The size of the indirect layout is + * the same as the size of the interface layout. + * + * The third and fourth DIEs define interface reference types as a pointers to the + * underlying structure layout types. As with classes, there is an interface_pointer type + * for raw address references used to type local and param vars and an indirect_pointer type + * (see above) for interface references stored in static and instance fields. + * + * A second level 1 defines a pointer to this layout type. + * + * n.b. the name used in the interface_layout DIE is the Java array name. This is + * deliberately inconsistent with the Java naming where the name refers to the pointer type. + * As with normal objects an interface reference in a Java signature appears as a pointer to + * an interface layout when printed by gdb. + * *
      * *
    • abbrev_code == interface_pointer, tag == pointer_type, has_children @@ -619,7 +697,7 @@ public int writeAbbrevs(DebugContext context, byte[] buffer, int p) { pos = writeVoidTypeAbbrev(context, buffer, pos); pos = writeObjectHeaderAbbrev(context, buffer, pos); - pos = writeClassLayoutAbbrev(context, buffer, pos); + pos = writeClassLayoutAbbrevs(context, buffer, pos); pos = writeClassReferenceAbbrev(context, buffer, pos); pos = writeMethodDeclarationAbbrevs(context, buffer, pos); pos = writeFieldDeclarationAbbrevs(context, buffer, pos); @@ -635,6 +713,20 @@ public int writeAbbrevs(DebugContext context, byte[] buffer, int p) { pos = writeSuperReferenceAbbrev(context, buffer, pos); pos = writeInterfaceImplementorAbbrev(context, buffer, pos); + /* + * 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 + * that gdb converts offsets embedded in static or instance fields to raw pointers. + * Transformed addresses are typed using pointers to the underlying layout. + * + * if address rebasing is not required then we a data_location attribute on the layout type + * will ensure that address tag bits are removed. + */ + if (dwarfSections.useHeapBase()) { + pos = writeIndirectLayoutAbbrev(context, buffer, pos); + pos = writeIndirectReferenceAbbrev(context, buffer, pos); + } + pos = writeParameterDeclarationAbbrevs(context, buffer, pos); return pos; } @@ -769,8 +861,6 @@ private int writeObjectHeaderAbbrev(@SuppressWarnings("unused") DebugContext con pos = writeAttrForm(DwarfDebugInfo.DW_FORM_strp, buffer, pos); pos = writeAttrType(DwarfDebugInfo.DW_AT_byte_size, buffer, pos); pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data1, buffer, pos); - pos = writeAttrType(DwarfDebugInfo.DW_AT_data_location, buffer, pos); - pos = writeAttrForm(DwarfDebugInfo.DW_FORM_expr_loc, buffer, pos); /* * Now terminate. */ @@ -779,10 +869,19 @@ private int writeObjectHeaderAbbrev(@SuppressWarnings("unused") DebugContext con return pos; } - private int writeClassLayoutAbbrev(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { + private int writeClassLayoutAbbrevs(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { int pos = p; + pos = writeClassLayoutAbbrev(context, DwarfDebugInfo.DW_ABBREV_CODE_class_layout1, buffer, pos); + if (!dwarfSections.useHeapBase()) { + pos = writeClassLayoutAbbrev(context, DwarfDebugInfo.DW_ABBREV_CODE_class_layout2, buffer, pos); + } + return pos; + } - pos = writeAbbrevCode(DwarfDebugInfo.DW_ABBREV_CODE_class_layout, buffer, pos); + private int writeClassLayoutAbbrev(@SuppressWarnings("unused") DebugContext context, int abbrevCode, byte[] buffer, int p) { + int pos = p; + + pos = writeAbbrevCode(abbrevCode, buffer, pos); pos = writeTag(DwarfDebugInfo.DW_TAG_class_type, buffer, pos); pos = writeFlag(DwarfDebugInfo.DW_CHILDREN_yes, buffer, pos); pos = writeAttrType(DwarfDebugInfo.DW_AT_name, buffer, pos); @@ -796,14 +895,16 @@ private int writeClassLayoutAbbrev(@SuppressWarnings("unused") DebugContext cont pos = writeAttrType(DwarfDebugInfo.DW_AT_decl_line, buffer, pos); pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data2, buffer, pos); */ - /* Add a data location expression to mask and/or rebase oop pointers. */ - pos = writeAttrType(DwarfDebugInfo.DW_AT_data_location, buffer, pos); - pos = writeAttrForm(DwarfDebugInfo.DW_FORM_expr_loc, buffer, pos); + if (abbrevCode == DwarfDebugInfo.DW_ABBREV_CODE_class_layout2) { + pos = writeAttrType(DwarfDebugInfo.DW_AT_data_location, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_expr_loc, buffer, pos); + } /* * Now terminate. */ pos = writeAttrType(DwarfDebugInfo.DW_AT_null, buffer, pos); pos = writeAttrForm(DwarfDebugInfo.DW_FORM_null, buffer, pos); + return pos; } @@ -937,9 +1038,6 @@ private int writeArrayLayoutAbbrev(@SuppressWarnings("unused") DebugContext cont pos = writeAttrForm(DwarfDebugInfo.DW_FORM_strp, buffer, pos); pos = writeAttrType(DwarfDebugInfo.DW_AT_byte_size, buffer, pos); pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data2, buffer, pos); - /* Add a data location expression to mask and/or rebase oop pointers. */ - pos = writeAttrType(DwarfDebugInfo.DW_AT_data_location, buffer, pos); - pos = writeAttrForm(DwarfDebugInfo.DW_FORM_expr_loc, buffer, pos); /* * Now terminate. */ @@ -974,9 +1072,6 @@ private int writeInterfaceLayoutAbbrev(@SuppressWarnings("unused") DebugContext pos = writeFlag(DwarfDebugInfo.DW_CHILDREN_yes, buffer, pos); pos = writeAttrType(DwarfDebugInfo.DW_AT_name, buffer, pos); pos = writeAttrForm(DwarfDebugInfo.DW_FORM_strp, buffer, pos); - /* Add a data location expression to mask and/or rebase oop pointers. */ - pos = writeAttrType(DwarfDebugInfo.DW_AT_data_location, buffer, pos); - pos = writeAttrForm(DwarfDebugInfo.DW_FORM_expr_loc, buffer, pos); /* * Now terminate. */ @@ -1127,6 +1222,55 @@ private int writeSuperReferenceAbbrev(@SuppressWarnings("unused") DebugContext c return pos; } + private int writeIndirectLayoutAbbrev(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { + int pos = p; + + /* + * oops are not necessarily raw addresses. they may contains pointer bits or be offsets from + * a base register. An indirect layout wraps a standard layout adding a data_location that + * translates indirect an oop to a raw address. It is used as the base for an indirect + * pointer type that is used to type values that need translation to a raw address i.e. + * values stored in static and instance fields. + */ + /* the type ofr an indirect layout that includes address translation info */ + pos = writeAbbrevCode(DwarfDebugInfo.DW_ABBREV_CODE_indirect_layout, buffer, pos); + pos = writeTag(DwarfDebugInfo.DW_TAG_class_type, buffer, pos); + pos = writeFlag(DwarfDebugInfo.DW_CHILDREN_yes, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_name, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_strp, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_byte_size, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data2, buffer, pos); + /* Add a data location expression to rebase oop pointers stored as offsets. */ + pos = writeAttrType(DwarfDebugInfo.DW_AT_data_location, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_expr_loc, buffer, pos); + /* + * Now terminate. + */ + pos = writeAttrType(DwarfDebugInfo.DW_AT_null, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_null, buffer, pos); + + return pos; + } + + private int writeIndirectReferenceAbbrev(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { + int pos = p; + + /* The type for a pointer to the indirect layout type. */ + pos = writeAbbrevCode(DwarfDebugInfo.DW_ABBREV_CODE_indirect_pointer, buffer, pos); + pos = writeTag(DwarfDebugInfo.DW_TAG_pointer_type, buffer, pos); + pos = writeFlag(DwarfDebugInfo.DW_CHILDREN_no, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_byte_size, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data1, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_type, 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 writeParameterDeclarationAbbrevs(DebugContext context, byte[] buffer, int p) { int pos = p; pos = writeParameterDeclarationAbbrev(context, DwarfDebugInfo.DW_ABBREV_CODE_method_parameter_declaration1, 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 8f30d867768f..17fe7eaa982a 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 @@ -73,29 +73,32 @@ public class DwarfDebugInfo extends DebugInfoBase { 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_layout = 8; - public static final int DW_ABBREV_CODE_class_pointer = 9; - public static final int DW_ABBREV_CODE_method_location = 10; - public static final int DW_ABBREV_CODE_static_field_location = 11; - public static final int DW_ABBREV_CODE_array_layout = 12; - public static final int DW_ABBREV_CODE_array_pointer = 13; - public static final int DW_ABBREV_CODE_interface_layout = 14; - public static final int DW_ABBREV_CODE_interface_pointer = 15; + 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_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; /* Level 2 DIEs. */ - public static final int DW_ABBREV_CODE_method_declaration1 = 16; - public static final int DW_ABBREV_CODE_method_declaration2 = 17; - public static final int DW_ABBREV_CODE_field_declaration1 = 18; - public static final int DW_ABBREV_CODE_field_declaration2 = 19; - public static final int DW_ABBREV_CODE_field_declaration3 = 20; - public static final int DW_ABBREV_CODE_field_declaration4 = 21; - public static final int DW_ABBREV_CODE_header_field = 22; - public static final int DW_ABBREV_CODE_array_data_type = 23; - public static final int DW_ABBREV_CODE_super_reference = 24; - public static final int DW_ABBREV_CODE_interface_implementor = 25; + 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_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; /* Level 3 DIEs. */ - public static final int DW_ABBREV_CODE_method_parameter_declaration1 = 26; - public static final int DW_ABBREV_CODE_method_parameter_declaration2 = 27; - public static final int DW_ABBREV_CODE_method_parameter_declaration3 = 28; + 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; /* * Define all the Dwarf tags we need for our DIEs. */ @@ -234,8 +237,9 @@ public class DwarfDebugInfo extends DebugInfoBase { public static final byte DW_OP_dup = 0x12; public static final byte DW_OP_and = 0x1a; public static final byte DW_OP_not = 0x20; - public static final byte DW_OP_shl = 0x20; public static final byte DW_OP_plus = 0x22; + public static final byte DW_OP_shl = 0x24; + public static final byte DW_OP_shr = 0x25; public static final byte DW_OP_bra = 0x28; public static final byte DW_OP_eq = 0x29; public static final byte DW_OP_lit0 = 0x30; @@ -249,6 +253,14 @@ public class DwarfDebugInfo extends DebugInfoBase { public static final byte rheapbase_x86 = (byte) 14; public static final byte rthread_x86 = (byte) 15; + /* + * prefix used to label indirect types used to ensure gdb performs oop reference --> raw address + * translation + */ + public static final String INDIRECT_PREFIX = "_z_."; + /* name of type for hub field which needs special case processing to remove tag bits */ + public static final String HUB_TYPE_NAME = "java.lang.Class"; + private DwarfStrSectionImpl dwarfStrSection; private DwarfAbbrevSectionImpl dwarfAbbrevSection; private DwarfInfoSectionImpl dwarfInfoSection; @@ -333,6 +345,18 @@ static class DwarfTypeProperties { * index in debug_info section of type declaration for this class. */ private int typeInfoIndex; + /** + * index in debug_info section of indirect type declaration for this class. + * + * this is normally just the same as the index of the normal type declaration, however, when + * oops are stored in static and instance fields as offsets from the heapbase register gdb + * needs to be told how to convert these oops to raw addresses and this requires attaching a + * data_location address translation expression to an indirect type that wraps the object + * layout type. so, with that encoding this field will identify the wrapper type whenever + * the original type is an object, interface or array layout. primitive types and header + * types do not need translating. + */ + private int indirectTypeInfoIndex; /** * The type entry with which these properties are associated. */ @@ -346,6 +370,14 @@ public void setTypeInfoIndex(int typeInfoIndex) { this.typeInfoIndex = typeInfoIndex; } + public int getIndirectTypeInfoIndex() { + return indirectTypeInfoIndex; + } + + public void setIndirectTypeInfoIndex(int typeInfoIndex) { + this.indirectTypeInfoIndex = typeInfoIndex; + } + public TypeEntry getTypeEntry() { return typeEntry; } @@ -353,6 +385,7 @@ public TypeEntry getTypeEntry() { DwarfTypeProperties(TypeEntry typeEntry) { this.typeEntry = typeEntry; this.typeInfoIndex = -1; + this.indirectTypeInfoIndex = -1; } } @@ -374,6 +407,10 @@ static class DwarfClassProperties extends DwarfTypeProperties { * Index of the class entry's class_layout DIE in the debug_info section. */ private int layoutIndex; + /** + * Index of the class entry's indirect layout DIE in the debug_info section. + */ + private int indirectLayoutIndex; /** * Index into debug_line section for associated compilation unit. */ @@ -400,6 +437,7 @@ static class DwarfClassProperties extends DwarfTypeProperties { this.cuIndex = -1; this.deoptCUIndex = -1; this.layoutIndex = -1; + this.indirectLayoutIndex = -1; this.lineIndex = -1; this.linePrologueSize = -1; this.lineSectionSize = -1; @@ -482,6 +520,22 @@ int getTypeIndex(DwarfTypeProperties typeProperties) { return typeProperties.getTypeInfoIndex(); } + void setIndirectTypeIndex(TypeEntry typeEntry, int idx) { + DwarfTypeProperties typeProperties = lookupTypeProperties(typeEntry); + assert typeProperties.getIndirectTypeInfoIndex() == -1 || typeProperties.getIndirectTypeInfoIndex() == idx; + typeProperties.setIndirectTypeInfoIndex(idx); + } + + int getIndirectTypeIndex(String typeName) { + DwarfTypeProperties typeProperties = lookupTypeProperties(typeName); + return getIndirectTypeIndex(typeProperties); + } + + int getIndirectTypeIndex(DwarfTypeProperties typeProperties) { + assert typeProperties.getIndirectTypeInfoIndex() >= 0; + return typeProperties.getIndirectTypeInfoIndex(); + } + void setCUIndex(ClassEntry classEntry, int idx) { DwarfClassProperties classProperties = lookupClassProperties(classEntry); assert classProperties.getTypeEntry() == classEntry; @@ -528,6 +582,21 @@ int getLayoutIndex(ClassEntry classEntry) { return classProperties.layoutIndex; } + void setIndirectLayoutIndex(ClassEntry classEntry, int idx) { + DwarfClassProperties classProperties = lookupClassProperties(classEntry); + assert classProperties.getTypeEntry() == classEntry; + assert classProperties.indirectLayoutIndex == -1 || classProperties.indirectLayoutIndex == idx; + classProperties.indirectLayoutIndex = idx; + } + + int getIndirectLayoutIndex(ClassEntry classEntry) { + DwarfClassProperties classProperties; + classProperties = lookupClassProperties(classEntry); + assert classProperties.getTypeEntry() == classEntry; + assert classProperties.indirectLayoutIndex >= 0; + return classProperties.indirectLayoutIndex; + } + void setLineIndex(ClassEntry classEntry, int idx) { DwarfClassProperties classProperties; classProperties = lookupClassProperties(classEntry); 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 c84f90f09ef6..c3ac6ec863bc 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 @@ -66,7 +66,7 @@ public class DwarfInfoSectionImpl extends DwarfSectionImpl { public static final String ARRAY_HEADER_STRUCT_NAME = "_arrhdr"; /** - * an info header section always contains a fixed number of bytes. + * An info header section always contains a fixed number of bytes. */ private static final int DW_DIE_HEADER_SIZE = 11; @@ -248,6 +248,11 @@ public int writePrimitiveType(DebugContext context, PrimitiveTypeEntry primitive log(context, " [0x%08x] primitive type %s", pos, primitiveTypeEntry.getTypeName()); /* Record the location of this type entry. */ setTypeIndex(primitiveTypeEntry, pos); + /* + * primitive fields never need an indirection so use the same index for places where we + * might want an indirect type + */ + setIndirectTypeIndex(primitiveTypeEntry, pos); int abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_primitive_type; log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); pos = writeAbbrevCode(abbrevCode, buffer, pos); @@ -271,6 +276,11 @@ public int writeVoidType(DebugContext context, PrimitiveTypeEntry primitiveTypeE log(context, " [0x%08x] primitive type void", pos); /* Record the location of this type entry. */ setTypeIndex(primitiveTypeEntry, pos); + /* + * Type void never needs an indirection so use the same index for places where we might want + * an indirect type. + */ + setIndirectTypeIndex(primitiveTypeEntry, pos); int abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_void_type; log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); pos = writeAbbrevCode(abbrevCode, buffer, pos); @@ -286,6 +296,11 @@ public int writeHeaderType(DebugContext context, HeaderTypeEntry headerTypeEntry log(context, " [0x%08x] header type %s", pos, name); /* Record the location of this type entry. */ setTypeIndex(headerTypeEntry, pos); + /* + * Header records don't need an indirection so use the same index for places where we might + * want an indirect type. + */ + setIndirectTypeIndex(headerTypeEntry, pos); int abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_object_header; log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); pos = writeAbbrevCode(abbrevCode, buffer, pos); @@ -293,9 +308,6 @@ public int writeHeaderType(DebugContext context, HeaderTypeEntry headerTypeEntry pos = writeAttrStrp(name, buffer, pos); log(context, " [0x%08x] byte_size 0x%x", pos, size); pos = writeAttrData1(size, buffer, pos); - /* Write a data location expression to mask and/or rebase oop pointers. */ - log(context, " [0x%08x] data_location", pos); - pos = writeOopRelocationExpression(buffer, pos); pos = writeHeaderFields(context, headerTypeEntry, buffer, pos); /* * Write a terminating null attribute. @@ -314,7 +326,8 @@ private int writeHeaderField(DebugContext context, FieldEntry fieldEntry, byte[] String fieldName = fieldEntry.fieldName(); TypeEntry valueType = fieldEntry.getValueType(); String valueTypeName = valueType.getTypeName(); - int valueTypeIdx = getTypeIndex(valueTypeName); + /* use the indirect type for the field so pointers get translated */ + int valueTypeIdx = getIndirectTypeIndex(valueTypeName); log(context, " [0x%08x] header field", pos); int abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_header_field; log(context, " [0x%08x] <2> Abbrev Number %d", pos, abbrevCode); @@ -480,9 +493,17 @@ private int writePrimaryClassUnit(DebugContext context, ClassEntry classEntry, b private int writeClassLayout(DebugContext context, ClassEntry classEntry, byte[] buffer, int p) { int pos = p; - setLayoutIndex(classEntry, pos); + int layoutIndex = pos; + setLayoutIndex(classEntry, layoutIndex); log(context, " [0x%08x] class layout", pos); - int abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_class_layout; + int abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_class_layout1; + /* + * when we don't have a separate indirect type then hub layouts need an extra data_location + * attribute + */ + if (!dwarfSections.useHeapBase() && dwarfSections.isHubClassEntry(classEntry)) { + abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_class_layout2; + } log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); pos = writeAbbrevCode(abbrevCode, buffer, pos); String name = classEntry.getTypeName(); @@ -494,9 +515,11 @@ private int writeClassLayout(DebugContext context, ClassEntry classEntry, byte[] int fileIdx = classEntry.localFilesIdx(classEntry.getFileEntry()); log(context, " [0x%08x] file 0x%x (%s)", pos, fileIdx, classEntry.getFileName()); pos = writeAttrData2((short) fileIdx, buffer, pos); - /* Write a data location expression to mask and/or rebase oop pointers. */ - log(context, " [0x%08x] data_location", pos); - pos = writeOopRelocationExpression(buffer, pos); + if (abbrevCode == DwarfDebugInfo.DW_ABBREV_CODE_class_layout2) { + /* Write a data location expression to mask and/or rebase oop pointers. */ + log(context, " [0x%08x] data_location", pos); + pos = writeIndirectOopConversionExpression(true, buffer, pos); + } int superTypeOffset; String superName; ClassEntry superClassEntry = classEntry.getSuperClass(); @@ -516,7 +539,36 @@ private int writeClassLayout(DebugContext context, ClassEntry classEntry, byte[] /* * Write a terminating null attribute. */ - return writeAttrNull(buffer, pos); + pos = writeAttrNull(buffer, pos); + + if (dwarfSections.useHeapBase()) { + /* + * Write a wrapper type with a data_location attribute that can act as a target for an + * indirect pointer. + */ + setIndirectLayoutIndex(classEntry, pos); + log(context, " [0x%08x] indirect class layout", pos); + abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_indirect_layout; + log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); + pos = writeAbbrevCode(abbrevCode, buffer, pos); + String indirectName = uniqueDebugString(DwarfDebugInfo.INDIRECT_PREFIX + classEntry.getTypeName()); + log(context, " [0x%08x] name 0x%x (%s)", pos, debugStringIndex(indirectName), name); + pos = writeAttrStrp(indirectName, buffer, pos); + log(context, " [0x%08x] byte_size 0x%x", pos, size); + pos = writeAttrData2((short) size, buffer, pos); + /* Write a data location expression to mask and/or rebase oop pointers. */ + log(context, " [0x%08x] data_location", pos); + pos = writeIndirectOopConversionExpression(dwarfSections.isHubClassEntry(classEntry), buffer, pos); + superTypeOffset = layoutIndex; + /* Now write the child field. */ + pos = writeSuperReference(context, superTypeOffset, superName, buffer, pos); + /* + * Write a terminating null attribute. + */ + pos = writeAttrNull(buffer, pos); + } + + return pos; } private int writeSuperReference(DebugContext context, int superTypeOffset, String superName, byte[] buffer, int p) { @@ -582,8 +634,8 @@ private int writeField(DebugContext context, ClassEntry classEntry, FieldEntry f /* At present we definitely don't have line numbers. */ } String valueTypeName = fieldEntry.getValueType().getTypeName(); - /* Static fields never store compressed values instance fields may do. */ - int typeIdx = getTypeIndex(valueTypeName); + /* use the indirect type for the field so pointers get translated if needed */ + int typeIdx = getIndirectTypeIndex(valueTypeName); log(context, " [0x%08x] type 0x%x (%s)", pos, typeIdx, valueTypeName); pos = writeAttrRefAddr(typeIdx, buffer, pos); if (!isStatic) { @@ -727,11 +779,8 @@ private int writeInterfaceLayout(DebugContext context, InterfaceClassEntry inter String name = interfaceClassEntry.getTypeName(); log(context, " [0x%08x] name 0x%x (%s)", pos, debugStringIndex(name), name); pos = writeAttrStrp(name, buffer, pos); - /* Write a data location expression to mask and/or rebase oop pointers. */ - log(context, " [0x%08x] data_location", pos); - pos = writeOopRelocationExpression(buffer, pos); /* - * now write references to all class layouts that implement this interface + * Now write references to all class layouts that implement this interface. */ pos = writeInterfaceImplementors(context, interfaceClassEntry, buffer, pos); pos = writeMethodDeclarations(context, interfaceClassEntry, buffer, pos); @@ -739,7 +788,36 @@ private int writeInterfaceLayout(DebugContext context, InterfaceClassEntry inter /* * Write a terminating null attribute. */ - return writeAttrNull(buffer, pos); + pos = writeAttrNull(buffer, pos); + + if (dwarfSections.useHeapBase()) { + /* + * Write a wrapper type with a data_location attribute that can act as a target for an + * indirect pointer. + */ + setIndirectLayoutIndex(interfaceClassEntry, pos); + log(context, " [0x%08x] indirect class layout", pos); + abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_indirect_layout; + log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); + pos = writeAbbrevCode(abbrevCode, buffer, pos); + String indirectName = uniqueDebugString(DwarfDebugInfo.INDIRECT_PREFIX + interfaceClassEntry.getTypeName()); + log(context, " [0x%08x] name 0x%x (%s)", pos, debugStringIndex(indirectName), name); + pos = writeAttrStrp(indirectName, buffer, pos); + int size = interfaceClassEntry.getSize(); + log(context, " [0x%08x] byte_size 0x%x", pos, size); + pos = writeAttrData2((short) size, buffer, pos); + /* Write a data location expression to mask and/or rebase oop pointers. */ + log(context, " [0x%08x] data_location", pos); + pos = writeIndirectOopConversionExpression(false, buffer, pos); + /* Now write the child field. */ + pos = writeSuperReference(context, layoutOffset, name, buffer, pos); + /* + * Write a terminating null attribute. + */ + pos = writeAttrNull(buffer, pos); + } + + return pos; } private int writeInterfaceImplementors(DebugContext context, InterfaceClassEntry interfaceClassEntry, byte[] buffer, int p) { @@ -770,18 +848,35 @@ private int writeClassType(DebugContext context, ClassEntry classEntry, byte[] b int pos = p; /* Define a pointer type referring to the underlying layout. */ - setTypeIndex(classEntry, pos); + int typeIdx = pos; + setTypeIndex(classEntry, typeIdx); log(context, " [0x%08x] class pointer type", pos); int abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_class_pointer; log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); pos = writeAbbrevCode(abbrevCode, buffer, pos); - int byteSize = dwarfSections.oopReferenceByteCount(); + int byteSize = dwarfSections.oopReferenceSize(); log(context, " [0x%08x] byte_size 0x%x", pos, byteSize); pos = writeAttrData1((byte) byteSize, buffer, pos); int layoutOffset = getLayoutIndex(classEntry); log(context, " [0x%08x] type 0x%x", pos, layoutOffset); pos = writeAttrRefAddr(layoutOffset, buffer, pos); + if (dwarfSections.useHeapBase()) { + /* Define an indirect pointer type referring to the indirect layout. */ + setIndirectTypeIndex(classEntry, pos); + log(context, " [0x%08x] class indirect pointer type", pos); + abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_indirect_pointer; + log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); + pos = writeAbbrevCode(abbrevCode, buffer, pos); + log(context, " [0x%08x] byte_size 0x%x", pos, byteSize); + pos = writeAttrData1((byte) byteSize, buffer, pos); + layoutOffset = getIndirectLayoutIndex(classEntry); + log(context, " [0x%08x] type 0x%x", pos, layoutOffset); + pos = writeAttrRefAddr(layoutOffset, buffer, pos); + } else { + setIndirectTypeIndex(classEntry, typeIdx); + } + return pos; } @@ -789,18 +884,35 @@ private int writeInterfaceType(DebugContext context, InterfaceClassEntry interfa int pos = p; /* Define a pointer type referring to the underlying layout. */ - setTypeIndex(interfaceClassEntry, pos); + int typeIdx = pos; + setTypeIndex(interfaceClassEntry, typeIdx); log(context, " [0x%08x] interface pointer type", pos); int abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_interface_pointer; log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); pos = writeAbbrevCode(abbrevCode, buffer, pos); - int byteSize = dwarfSections.oopReferenceByteCount(); + int byteSize = dwarfSections.oopReferenceSize(); log(context, " [0x%08x] byte_size 0x%x", pos, byteSize); pos = writeAttrData1((byte) byteSize, buffer, pos); int layoutOffset = getLayoutIndex(interfaceClassEntry); log(context, " [0x%08x] type 0x%x", pos, layoutOffset); pos = writeAttrRefAddr(layoutOffset, buffer, pos); + if (dwarfSections.useHeapBase()) { + /* Define an indirect pointer type referring to the indirect layout. */ + setIndirectTypeIndex(interfaceClassEntry, pos); + log(context, " [0x%08x] interface indirect pointer type", pos); + abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_indirect_pointer; + log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); + pos = writeAbbrevCode(abbrevCode, buffer, pos); + log(context, " [0x%08x] byte_size 0x%x", pos, byteSize); + pos = writeAttrData1((byte) byteSize, buffer, pos); + layoutOffset = getIndirectLayoutIndex(interfaceClassEntry); + log(context, " [0x%08x] type 0x%x", pos, layoutOffset); + pos = writeAttrRefAddr(layoutOffset, buffer, pos); + } else { + setIndirectTypeIndex(interfaceClassEntry, typeIdx); + } + return pos; } @@ -907,9 +1019,22 @@ private int writeArrayTypeUnit(DebugContext context, ArrayTypeEntry arrayTypeEnt log(context, " [0x%08x] name 0x%x (%s)", pos, debugStringIndex(name), name); pos = writeAttrStrp(name, buffer, pos); /* Write the array layout and array reference DIEs. */ - int layouIdx = pos; - pos = writeArrayLayout(context, arrayTypeEntry, buffer, pos); - pos = writeArrayType(context, arrayTypeEntry, layouIdx, buffer, pos); + TypeEntry elementType = arrayTypeEntry.getElementType(); + StructureTypeEntry headerType; + if (elementType.isPrimitive()) { + PrimitiveTypeEntry primitiveTypeEntry = (PrimitiveTypeEntry) elementType; + headerType = (StructureTypeEntry) lookupType(ARRAY_HEADER_STRUCT_NAME + primitiveTypeEntry.getTypeChar()); + } else { + headerType = (StructureTypeEntry) lookupType(ARRAY_HEADER_STRUCT_NAME + "A"); + } + int size = headerType.getSize(); + int layoutIdx = pos; + pos = writeArrayLayout(context, arrayTypeEntry, elementType, size, buffer, pos); + int indirectLayoutIdx = pos; + if (dwarfSections.useHeapBase()) { + pos = writeIndirectArrayLayout(context, arrayTypeEntry, size, layoutIdx, buffer, pos); + } + pos = writeArrayTypes(context, arrayTypeEntry, layoutIdx, indirectLayoutIdx, buffer, pos); /* * Write a terminating null attribute. */ @@ -921,16 +1046,8 @@ private int writeArrayTypeUnit(DebugContext context, ArrayTypeEntry arrayTypeEnt return pos; } - private int writeArrayLayout(DebugContext context, ArrayTypeEntry arrayTypeEntry, byte[] buffer, int p) { + private int writeArrayLayout(DebugContext context, ArrayTypeEntry arrayTypeEntry, TypeEntry elementType, int size, byte[] buffer, int p) { int pos = p; - TypeEntry elementType = arrayTypeEntry.getElementType(); - StructureTypeEntry headerType; - if (elementType.isPrimitive()) { - PrimitiveTypeEntry primitiveTypeEntry = (PrimitiveTypeEntry) elementType; - headerType = (StructureTypeEntry) lookupType(ARRAY_HEADER_STRUCT_NAME + primitiveTypeEntry.getTypeChar()); - } else { - headerType = (StructureTypeEntry) lookupType(ARRAY_HEADER_STRUCT_NAME + "A"); - } log(context, " [0x%08x] array layout", pos); int abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_array_layout; log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); @@ -938,12 +1055,8 @@ private int writeArrayLayout(DebugContext context, ArrayTypeEntry arrayTypeEntry String name = arrayTypeEntry.getTypeName(); log(context, " [0x%08x] name 0x%x (%s)", pos, debugStringIndex(name), name); pos = writeAttrStrp(name, buffer, pos); - int size = headerType.getSize(); log(context, " [0x%08x] byte_size 0x%x", pos, size); pos = writeAttrData2((short) size, buffer, pos); - /* Write a data location expression to mask and/or rebase oop pointers. */ - log(context, " [0x%08x] data_location", pos); - pos = writeOopRelocationExpression(buffer, pos); /* Now the child DIEs. */ @@ -959,6 +1072,34 @@ private int writeArrayLayout(DebugContext context, ArrayTypeEntry arrayTypeEntry return writeAttrNull(buffer, pos); } + private int writeIndirectArrayLayout(DebugContext context, ArrayTypeEntry arrayTypeEntry, int size, int layoutOffset, byte[] buffer, int p) { + int pos = p; + + /* + * write a wrapper type with a data_location attribute that can act as a target for an + * indirect pointer + */ + log(context, " [0x%08x] indirect class layout", pos); + int abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_indirect_layout; + log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); + pos = writeAbbrevCode(abbrevCode, buffer, pos); + String name = arrayTypeEntry.getTypeName(); + String indirectName = uniqueDebugString(DwarfDebugInfo.INDIRECT_PREFIX + name); + log(context, " [0x%08x] name 0x%x (%s)", pos, debugStringIndex(indirectName), name); + pos = writeAttrStrp(indirectName, buffer, pos); + log(context, " [0x%08x] byte_size 0x%x", pos, size); + pos = writeAttrData2((short) size, buffer, pos); + /* Write a data location expression to mask and/or rebase oop pointers. */ + log(context, " [0x%08x] data_location", pos); + pos = writeIndirectOopConversionExpression(false, buffer, pos); + /* Now write the child field. */ + pos = writeSuperReference(context, layoutOffset, name, buffer, pos); + /* + * Write a terminating null attribute. + */ + return writeAttrNull(buffer, pos); + } + private int writeArrayDataType(DebugContext context, TypeEntry elementType, byte[] buffer, int p) { int pos = p; log(context, " [0x%08x] array element data type", pos); @@ -969,7 +1110,8 @@ private int writeArrayDataType(DebugContext context, TypeEntry elementType, byte log(context, " [0x%08x] byte_size 0x%x", pos, size); pos = writeAttrData1((byte) size, buffer, pos); String elementTypeName = elementType.getTypeName(); - int elementTypeIdx = getTypeIndex(elementTypeName); + /* use the indirect type for the element type so pointers get translated */ + int elementTypeIdx = getIndirectTypeIndex(elementTypeName); log(context, " [0x%08x] type idx 0x%x (%s)", pos, elementTypeIdx, elementTypeName); pos = writeAttrRefAddr(elementTypeIdx, buffer, pos); return pos; @@ -1020,22 +1162,38 @@ private int writeArraySuperReference(DebugContext context, ArrayTypeEntry arrayT return pos; } - private int writeArrayType(DebugContext context, ArrayTypeEntry arrayTypeEntry, int layoutOffset, byte[] buffer, int p) { + private int writeArrayTypes(DebugContext context, ArrayTypeEntry arrayTypeEntry, int layoutOffset, int indirectLayoutOffset, byte[] buffer, int p) { int pos = p; String name = uniqueDebugString(arrayTypeEntry.getTypeName()); + int typeIdx = pos; setTypeIndex(arrayTypeEntry, pos); /* Define a pointer type referring to the underlying layout. */ log(context, " [0x%08x] array pointer type", pos); int abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_array_pointer; log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); pos = writeAbbrevCode(abbrevCode, buffer, pos); - int byteSize = dwarfSections.oopReferenceByteCount(); + int byteSize = dwarfSections.oopReferenceSize(); log(context, " [0x%08x] byte_size 0x%x", pos, byteSize); pos = writeAttrData1((byte) byteSize, buffer, pos); log(context, " [0x%08x] type (pointer) 0x%x (%s)", pos, layoutOffset, name); pos = writeAttrRefAddr(layoutOffset, buffer, pos); + if (dwarfSections.useHeapBase()) { + setIndirectTypeIndex(arrayTypeEntry, pos); + /* Define an indirect pointer type referring to the underlying indirect layout. */ + log(context, " [0x%08x] array indirect pointer type", pos); + abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_indirect_pointer; + log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); + pos = writeAbbrevCode(abbrevCode, buffer, pos); + log(context, " [0x%08x] byte_size 0x%x", pos, byteSize); + pos = writeAttrData1((byte) byteSize, buffer, pos); + log(context, " [0x%08x] type (pointer) 0x%x (%s)", pos, indirectLayoutOffset, name); + pos = writeAttrRefAddr(indirectLayoutOffset, buffer, pos); + } else { + setIndirectTypeIndex(arrayTypeEntry, typeIdx); + } + return pos; } @@ -1201,78 +1359,186 @@ public int writeAttrAccessibility(int modifiers, byte[] buffer, int p) { return writeAttrData1(access, buffer, p); } - public int writeOopRelocationExpression(byte[] buffer, int p) { + public int writeIndirectOopConversionExpression(boolean isHub, byte[] buffer, int p) { int pos = p; - if (dwarfSections.useHeapBase()) { + /* + * The conversion rules are different depending on whether they apply to the hub class or + * any other class. + * + * They also vary according to whether isolates are in use and, if so, whether it is + * combined with compression. + * + * Finally, they depend on the choice of GC, specifically it's influence on the number of GC + * tag bits. + * + * The rules are as follows: + * + * H:-SpawnIsolates (explicitly disabled isolates support) + * + *
      • Regular oops: address64 = val64
      • Oops pointing to hubs: address64 = val64 & + * "GC-bits bitmask"
      + * + * -H:+SpawnIsolates -H:-UseCompressedReferences (CE default) + * + *
      • Regular oops: address64 = val64 + r14
      • Oops pointing to hubs: address64 = + * ((val64 >> "num GC bits") << "objectAlignmentBits") + r14
      + * + * objectAlignmentBits should always be 3 + * + * -H:+SpawnIsolates+ -H:+UseCompressedReferences (EE default) + * + *
      • Regular oops: address64 = (val32 << "compressShift") + r14
      • Oops pointing + * to hubs: address64 = ((val32 >> "num GC bits") << "compressShift") + r14
      + * + * compressShift should always be 3. + * + * For Serial garbage collector (CE) + * + *
      • "num GC bits": 3
      • "GC-bits bitmask": ~0b111
      + * + * For G1 garbage collector (EE only) + * + *
      • "num GC bits": 5
      • "GC-bits bitmask": ~0b11111
      + * + * n.b. + * + * The setting for option -H:+/-SpawnIsolates is determined by useHeapBase == true/false. + * + * The setting for option -H:+/-UseCompressedReferences is determined by oopShiftCount == + * zero/non-zero + */ + + boolean useHeapBase = dwarfSections.useHeapBase(); + int oopCompressShift = dwarfSections.oopCompressShift(); + int oopTagsShift = dwarfSections.oopTagsShift(); + int oopAlignShift = dwarfSections.oopAlignShift(); + /* we may be able to use a mask or a right shift then a left shift or just a left shift */ + int mask = 0; + int rightShift = 0; + int leftShift = 0; + int exprSize = 0; + + /* + * First we compute the size of the locexpr and decide how to do any required bit-twiddling + */ + if (!useHeapBase) { + /* We must be compressing for a hub otherwise this call would not be needed. */ + assert isHub == true; + mask = dwarfSections.oopTagsMask(); + assert mask != 0; /*- - * oop is 32 bit signed offset, possibly shifted by CompressEncoding.getShift() + * We don't need to care about zero oops just mask off the tag bits. * - * .... push object address (1 byte) ...... [offset] - * .... duplicate object base ............. [offset, offset] - * .... breq end .......................... [offset] - * .... __optional_begin__ (if shift != 0) - * .... push compress_shift ............... [offset, shift] - * .... lsh ............................... [new_offset] - * .... __optional_end__ - * .... push rheap + 0 .................... [rheap] - * .... ADD ............................... [oop] - * end: ................................... [offset=0 | oop] + * required expression is + * + * .... push object address .. (1 byte) ..... [tagged oop] + * .... push mask ............ (1 byte) ..... [tagged oop, mask] + * .... NOT .................. (1 byte) ..... [tagged oop, ~mask] + * .... AND .................. (1 byte) ..... [raw oop] */ - - /* Write a location rebasing the offset relative to the heapbase register. */ - byte regOp = (byte) (DwarfDebugInfo.DW_OP_breg0 + dwarfSections.getHeapbaseRegister()); - /* - * We have to size the DWARF expression by writing it to the scratch buffer so we can - * write its size as a ULEB before the expression itself. + exprSize += 4; + } else { + /*- + * required expression will be one of these paths + * + * .... push object address .. (1 byte) ..... [offset] + * .... duplicate object base (1 byte) ..... [offset, offset] + * .... push 0 ............... (1 byte) ..... [offset, offset, 0] + * .... eq ................... (1 byte) ..... [offset] + * .... brtrue end ........... (3 bytes) .... [offset == oop == 0 if taken] + * IF mask != 0 + * .... push mask ............ (1 byte) ..... [offset, mask] + * .... NOT .................. (1 byte) ..... [offset, ~mask] + * .... AND .................. (1 byte) ..... [offset] + * ELSE + * IF rightShift != 0 + * .... push rightShift ...... (1 byte) ..... [offset, right shift] + * .... LSHR ................. (1 byte) ..... [offset] + * END IF + * IF leftShift != 0 + * .... push leftShift ....... (1 byte) ..... [offset, left shift] + * .... LSHL ................. (1 byte) ..... [offset] + * END IF + * END IF + * .... push rheap+0 ......... (2 bytes) .... [offset, rheap] + * .... ADD .................. (1 byte) ..... [oop] + * end: ...................................... [oop] + * */ - int shiftBitCount = dwarfSections.oopShiftBitCount(); - short skipBytes = (short) (shiftBitCount == 0 ? 3 : 5); - int size = 7 + skipBytes; - if (buffer == null) { - /* add ULEB size to the expression size. */ - return pos + putULEB(size, scratch, 0) + size; + /* Count all bytes in common path */ + exprSize += 10; + if (isHub) { + if (oopCompressShift == 0) { + /* We need to use oopAlignment for the shift. */ + oopCompressShift = oopAlignShift; + } + if (oopCompressShift == oopTagsShift) { + /* We can use a mask to remove the bits. */ + mask = dwarfSections.oopTagsMask(); + exprSize += 3; + } else { + /* We need two shifts to remove the bits. */ + rightShift = oopTagsShift; + leftShift = oopCompressShift; + exprSize += 4; + } } else { - /* write the size and expression into the output buffer. */ - pos = putULEB(size, buffer, pos); + /* No flags to deal with, so we need either an uncompress or nothing. */ + if (oopCompressShift != 0) { + leftShift = oopCompressShift; + exprSize += 2; + } + } + } + if (buffer == null) { + /* We need to write size as a ULEB then leave space for size instructions. */ + return pos + putULEB(exprSize, scratch, 0) + exprSize; + + } else { + /* Write size followed by the expression and check the size comes out correct. */ + pos = putULEB(exprSize, buffer, pos); + int exprStart = pos; + if (!useHeapBase) { pos = putByte(DwarfDebugInfo.DW_OP_push_object_address, buffer, pos); + pos = putByte((byte) (DwarfDebugInfo.DW_OP_lit0 + mask), buffer, pos); + pos = putByte(DwarfDebugInfo.DW_OP_not, buffer, pos); + pos = putByte(DwarfDebugInfo.DW_OP_and, buffer, pos); + } else { + pos = putByte(DwarfDebugInfo.DW_OP_push_object_address, buffer, pos); + /* skip to end if oop is null */ pos = putByte(DwarfDebugInfo.DW_OP_dup, buffer, pos); pos = putByte(DwarfDebugInfo.DW_OP_lit0, buffer, pos); pos = putByte(DwarfDebugInfo.DW_OP_eq, buffer, pos); + int skipStart = pos + 3; /* offset excludes BR op + 2 operand bytes */ + short offsetToEnd = (short) (exprSize - (skipStart - exprStart)); pos = putByte(DwarfDebugInfo.DW_OP_bra, buffer, pos); - pos = putShort(skipBytes, buffer, pos); - if (shiftBitCount > 0) { - putByte((byte) (DwarfDebugInfo.DW_OP_lit0 + shiftBitCount), buffer, pos); - putByte(DwarfDebugInfo.DW_OP_shl, buffer, pos); + pos = putShort(offsetToEnd, buffer, pos); + /* insert mask or shifts as necessary */ + if (mask != 0) { + pos = putByte((byte) (DwarfDebugInfo.DW_OP_lit0 + mask), buffer, pos); + pos = putByte(DwarfDebugInfo.DW_OP_not, buffer, pos); + pos = putByte(DwarfDebugInfo.DW_OP_and, buffer, pos); + } else { + if (rightShift != 0) { + pos = putByte((byte) (DwarfDebugInfo.DW_OP_lit0 + rightShift), buffer, pos); + pos = putByte(DwarfDebugInfo.DW_OP_shr, buffer, pos); + } + if (leftShift != 0) { + pos = putByte((byte) (DwarfDebugInfo.DW_OP_lit0 + leftShift), buffer, pos); + pos = putByte(DwarfDebugInfo.DW_OP_shl, buffer, pos); + } } + /* add the resulting offset to the heapbase register */ + byte regOp = (byte) (DwarfDebugInfo.DW_OP_breg0 + dwarfSections.getHeapbaseRegister()); pos = putByte(regOp, buffer, pos); pos = putSLEB(0, buffer, pos); /* 1 byte. */ - return putByte(DwarfDebugInfo.DW_OP_plus, buffer, pos); + pos = putByte(DwarfDebugInfo.DW_OP_plus, buffer, pos); + assert pos == skipStart + offsetToEnd; } - } else { - /*- - * oop is 64 bit pointer modulo low flag bits - * guaranteed: mask == 2^N - 1 where N <= 5 - * - * .... push object address .. [tagged_oop] - * .... push flag bits mask .. [tagged_oop, flag_bits_mask] - * .... NOT .................. [tagged_oop, oop_bits_mask] - * .... AND .................. [oop] - */ - - /* Write a relocatable address relative to the heap section start. */ - int size = 4; - /* Write the size and expression into the output buffer. */ - if (buffer == null) { - return pos + putULEB(size, scratch, 0) + size; - } else { - pos = putULEB(size, buffer, pos); - pos = putByte(DwarfDebugInfo.DW_OP_push_object_address, buffer, pos); - pos = putByte((byte) (DwarfDebugInfo.DW_OP_lit0 + dwarfSections.oopFlagBitsMask()), buffer, pos); - pos = putByte(DwarfDebugInfo.DW_OP_not, buffer, pos); - pos = putByte(DwarfDebugInfo.DW_OP_and, buffer, pos); - } - return pos; + /* make sure we added up correctly */ + assert pos == exprStart + exprSize; } + return pos; } /** diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSectionImpl.java index 30ef21713a4f..6594a571b98c 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 @@ -482,6 +482,17 @@ protected void setTypeIndex(TypeEntry typeEntry, int pos) { dwarfSections.setTypeIndex(typeEntry, pos); } + protected int getIndirectTypeIndex(String typeName) { + if (!contentByteArrayCreated()) { + return 0; + } + return dwarfSections.getIndirectTypeIndex(typeName); + } + + protected void setIndirectTypeIndex(TypeEntry typeEntry, int pos) { + dwarfSections.setIndirectTypeIndex(typeEntry, pos); + } + protected int getCUIndex(ClassEntry classEntry) { if (!contentByteArrayCreated()) { return 0; @@ -544,6 +555,17 @@ protected int getLayoutIndex(ClassEntry classEntry) { return dwarfSections.getLayoutIndex(classEntry); } + protected void setIndirectLayoutIndex(ClassEntry classEntry, int pos) { + dwarfSections.setIndirectLayoutIndex(classEntry, pos); + } + + protected int getIndirectLayoutIndex(ClassEntry classEntry) { + if (!contentByteArrayCreated()) { + return 0; + } + return dwarfSections.getIndirectLayoutIndex(classEntry); + } + protected void setLayoutIndex(ClassEntry classEntry, int pos) { dwarfSections.setLayoutIndex(classEntry, pos); } 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 5e61cbca78fe..ff6c6709e40f 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 @@ -92,9 +92,10 @@ class NativeImageDebugInfoProvider implements DebugInfoProvider { private final NativeImageCodeCache codeCache; @SuppressWarnings("unused") private final NativeImageHeap heap; boolean useHeapBase; - int heapShift; - int flagBitsMask; - int referenceByteCount; + int compressShift; + int tagsMask; + int referenceSize; + int referenceAlignment; int primitiveStartOffset; int referenceStartOffset; @@ -106,17 +107,17 @@ class NativeImageDebugInfoProvider implements DebugInfoProvider { ObjectHeader objectHeader = Heap.getHeap().getObjectHeader(); ObjectInfo primitiveFields = heap.getObjectInfo(StaticFieldsSupport.getStaticPrimitiveFields()); ObjectInfo objectFields = heap.getObjectInfo(StaticFieldsSupport.getStaticObjectFields()); - this.flagBitsMask = objectHeader.getReservedBitsMask(); + this.tagsMask = objectHeader.getReservedBitsMask(); if (SubstrateOptions.SpawnIsolates.getValue()) { CompressEncoding compressEncoding = ImageSingletons.lookup(CompressEncoding.class); this.useHeapBase = compressEncoding.hasBase(); - this.heapShift = (compressEncoding.hasShift() ? compressEncoding.getShift() : 0); - this.referenceByteCount = OBJECTLAYOUT.getReferenceSize(); + this.compressShift = (compressEncoding.hasShift() ? compressEncoding.getShift() : 0); } else { this.useHeapBase = false; - this.heapShift = 0; - this.referenceByteCount = 8; + this.compressShift = 0; } + this.referenceSize = OBJECTLAYOUT.getReferenceSize(); + this.referenceAlignment = OBJECTLAYOUT.getAlignment(); /* Offsets need to be adjusted relative to the heap base plus partition-specific offset. */ primitiveStartOffset = (int) primitiveFields.getOffset(); referenceStartOffset = (int) objectFields.getOffset(); @@ -128,18 +129,23 @@ public boolean useHeapBase() { } @Override - public int oopShiftBitCount() { - return heapShift; + public int oopCompressShift() { + return compressShift; } @Override - public int oopReferenceByteCount() { - return referenceByteCount; + public int oopReferenceSize() { + return referenceSize; } @Override - public int oopFlagBitsMask() { - return flagBitsMask; + public int oopAlignment() { + return referenceAlignment; + } + + @Override + public int oopTagsMask() { + return tagsMask; } @Override From 20618672227ec0e86c17fc4953992966acf3c575 Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Fri, 5 Feb 2021 15:08:59 +0000 Subject: [PATCH 5/6] Make arrays subtype object and include length as a local field minor tweak to object sizing routines --- substratevm/DebugInfo.md | 183 ++++++++++-------- .../objectfile/debugentry/ArrayTypeEntry.java | 10 +- .../objectfile/debugentry/ClassEntry.java | 15 ++ .../objectfile/debugentry/DebugInfoBase.java | 10 +- .../debugentry/StructureTypeEntry.java | 11 +- .../debuginfo/DebugInfoProvider.java | 4 +- .../elf/dwarf/DwarfAbbrevSectionImpl.java | 2 +- .../objectfile/elf/dwarf/DwarfDebugInfo.java | 29 +-- .../elf/dwarf/DwarfInfoSectionImpl.java | 53 +++-- .../elf/dwarf/DwarfLineSectionImpl.java | 3 +- .../elf/dwarf/DwarfSectionImpl.java | 9 +- .../image/NativeImageDebugInfoProvider.java | 131 +++++++------ 12 files changed, 260 insertions(+), 200 deletions(-) diff --git a/substratevm/DebugInfo.md b/substratevm/DebugInfo.md index cc0185398cf6..987f1d94bd08 100644 --- a/substratevm/DebugInfo.md +++ b/substratevm/DebugInfo.md @@ -187,6 +187,7 @@ the hub field: (gdb) ptype _objhdr type = struct _objhdr { java.lang.Class *hub; + int idHash; } (gdb) ptype 'java.lang.Object' @@ -208,13 +209,14 @@ to the `byte[]` value array located in the name String. $2 = 0x7ffff7c01028 (gdb) print *$2->hub->name->value $3 = { - <_arrhdrB> = { - hub = 0x90e4b8, - len = 19, - idHash = 3759493 - }, + = { + <_objhdr> = { + hub = 0x942d40, + idHash = 1806863149 + }, }, members of byte []: - data = 0x904798 "[Ljava.lang.String;" + len = 19, + data = 0x923a90 "[Ljava.lang.String;" } ``` @@ -224,12 +226,13 @@ Casting it to this type shows it has length 1. ``` (gdb) print *('java.lang.String[]' *)$rdi $4 = { - <_arrhdrA> = { - hub = 0x906a78, - len = 1, - idHash = 0 - }, + = { + <_objhdr> = { + hub = 0x925be8, + idHash = 0 + }, }, members of java.lang.String[]: + len = 1, data = 0x7ffff7c01038 } ``` @@ -246,11 +249,11 @@ Indeed it is useful to define a gdb command `hubname_raw` to execute this operation on an arbitrary raw memory address ``` -command hubname_raw - x/s ((_objhdr *)($arg0))->hub->name->value->data +define hubname_raw + x/s (('java.lang.Object' *)($arg0))->hub->name->value->data end -(gdb) hubname_raw $2 +(gdb) hubname_raw $rdi 0x904798: "[Ljava.lang.String;" ``` @@ -264,37 +267,42 @@ $5 = 0x2 Cannot access memory at address 0x2 ``` -Array type layouts are also modelled with a class. The class inherits -fields from an array header struct specific to the array element type, -one of _arrhdrZ _arrhdrB, _arrhdrS, ... _arrhdrA (the last one is for -object arrays). Inherited fields include the hub, array length, idHash -to round up the header size to a boundary suitable for the array -element type. The array class (layout) type includes only one field, a -C++ array of length zero whose element type is a primtiive type or -Java referece type. +Array type layouts are modelled as a C++ class type. It inherits +from class Object so it includes the hub and idHash header fields +defined by _objhdr. It adds a length field and an embedded (C++) data +array whose elements are typed from the Java array's element type, +either primitive values or object references. ``` (gdb) ptype 'java.lang.String[]' -type = struct java.lang.String[] : public _arrhdrA { +type = class java.lang.String[] : public java.lang.Object { + int len; java.lang.String *data[0]; } ``` -Notice that the type of the values stored in the data array is -`java.lang.String *` i.e. the C++ array stores Java references -i.e. addresses as far as the C++ model is concerned. +The embedded array is nominally sized with length 0. However, when a +Java array instance is allocated it includes enough space to ensure +the data array can store the number of items defined in the length +field. -When gdb knows the Java type for a reference it can be printed without -casting using a simpler version of the hubname command. For example, -the String array retrieved above as $4 has a known type. +Notice that in this case the type of the values stored in the data +array is `java.lang.String *`. The the C++ array stores Java +object references i.e. addresses as far as the C++ model is +concerned. + +If gdb already knows the Java type for a reference it can be printed +without casting using a simpler version of the hubname command. For +example, the String array retrieved above as $4 has a known type. ``` (gdb) ptype $4 -type = struct java.lang.String[] : public _arrhdrA { +type = class java.lang.String[] : public java.lang.Object { + int len; java.lang.String *data[0]; } -command hubname +define hubname x/s (($arg0))->hub->name->value->data end @@ -302,31 +310,18 @@ end 0x923b68: "[Ljava.lang.String;" ``` -(notice that gdb automatically uses the address of the printed object) - -The array header structs are all extensions of the basic _objhdr type -which means that arrays and objects can both be safely cast to oops. - -``` -(gdb) ptype _arrhdrA -type = struct _arrhdrA { - java.lang.Class *hub; - int len; - int idHash; -} -``` - -Interfaces layouts are modelled as union types whose members are the -layouts for all the classes which implement the interfacse. +Interface layouts are modelled as C++ union types. The members of the +union include the C++ layout types for all Java classes which implement +the interface. ``` (gdb) ptype 'java.lang.CharSequence' -type = union _java.lang.CharSequence { - _java.lang.AbstractStringBuilder _java.lang.AbstractStringBuilder; - _java.lang.StringBuffer _java.lang.StringBuffer; - _java.lang.StringBuilder _java.lang.StringBuilder; - _java.lang.String _java.lang.String; - _java.nio.CharBuffer _java.nio.CharBuffer; +type = union java.lang.CharSequence { + java.nio.CharBuffer _java.nio.CharBuffer; + java.lang.AbstractStringBuilder _java.lang.AbstractStringBuilder; + java.lang.String _java.lang.String; + java.lang.StringBuilder _java.lang.StringBuilder; + java.lang.StringBuffer _java.lang.StringBuffer; } ``` @@ -337,16 +332,16 @@ If we take the first String in the args array we can ask gdb to cast it to interface CharSequence ``` (gdb) print (('java.lang.String[]' *)$rdi)->data[0] -$6 = (java.lang.String *) 0x7ffff7c01060 -(gdb) print ('java.lang.CharSequence' *)$6 -$7 = (java.lang.CharSequence *) 0x7ffff7c01060 +$5 = (java.lang.String *) 0x7ffff7c01060 +(gdb) print ('java.lang.CharSequence' *)$5 +$6 = (java.lang.CharSequence *) 0x7ffff7c01060 ``` The hubname command won't work with this union type because it is only objects of the elements of the union that include the hub field: ``` -(gdb) hubname $7 +(gdb) hubname $6 There is no member named hub. ``` @@ -355,9 +350,9 @@ can be passed to hubname in order to identify the actual type. This allows the correct union element to be selected: ``` -(gdb) hubname $7->'_java.nio.CharBuffer' +(gdb) hubname $6->'_java.nio.CharBuffer' 0x7d96d8: "java.lang.String\270", -(gdb) print $7->'_java.lang.String' +(gdb) print $6->'_java.lang.String' $18 = { = { <_objhdr> = { @@ -370,8 +365,9 @@ $18 = { } ``` -Notice that the printed class name includes some trailing characters. -That's because Java strings are not guaranteed to be zero-terminated. +Notice that the printed class name for the hub includes some trailing +characters. That's because a data array storing Java String text +is not guaranteed to be zero-terminated. The current debug info model does not include the location info needed to allow symbolic names for local vars and parameter vars to be @@ -430,9 +426,9 @@ the location (address) of the field in the heap. ``` (gdb) p 'java.math.BigInteger'::powerCache -$9 = (java.math.BigInteger[][] *) 0xa6fd98 +$8 = (java.math.BigInteger[][] *) 0xa6fd98 (gdb) p &'java.math.BigInteger'::powerCache -$10 = (java.math.BigInteger[][] **) 0xa6fbd8 +$9 = (java.math.BigInteger[][] **) 0xa6fbd8 ``` gdb dereferences through symbolic names for static fields to access @@ -440,25 +436,27 @@ the primitive value or object stored in the field ``` (gdb) p *'java.math.BigInteger'::powerCache -$11 = { - <_arrhdrA> = { +$10 = { + = { + <_objhdr> = { hub = 0x9ab3d0, - len = 37, idHash = 489620191 - }, + }, }, members of _java.math.BigInteger[][]: + len = 37, data = 0xa6fda8 } (gdb) p 'java.math.BigInteger'::powerCache->data[0]@4 -$12 = {0x0, 0x0, 0xc09378, 0xc09360} +$11 = {0x0, 0x0, 0xc09378, 0xc09360} (gdb) p *'java.math.BigInteger'::powerCache->data[2] -$13 = { - <_arrhdrA> = { +$12 = { + = { + <_objhdr> = { hub = 0x919898, - len = 1, idHash = 1796421813 - }, + }, }, members of java.math.BigInteger[]: + len = 1, data = 0xc09388 } (gdb) p *'java.math.BigInteger'::powerCache->data[2]->data[0] @@ -699,10 +697,10 @@ type = class _z_.java.math.BigInteger[][] : public java.math.BigInteger[][] { The field is typed as '_z_.java.math.BigInteger[][]' which is an empty wrapper class that inherits from the expected type 'java.math.BigInteger[][]'. This wrapper type is essentially the same -as the original but the DWARFINFO that defines it includes information -that tells gdb how to convert pointers to this type. +as the original but the DWARF info record that defines it includes +information that tells gdb how to convert pointers to this type. -If gdb is asked to print the oop stored in this field it is clear that +When gdb is asked to print the oop stored in this field it is clear that it is an offset rather than a raw address. ``` @@ -720,12 +718,13 @@ data. (gdb) p/x *'java.math.BigInteger'::powerCache $2 = { = { - <_arrhdrA> = { - hub = 0x1ec0e2, - idHash = 0x76381891, - len = 0x25 - }, + = { + <_objhdr> = { + hub = 0x1ec0e2, + idHash = 0x2f462321 + }, }, members of java.math.BigInteger[][]: + len = 0x25, data = 0x7ffff7a86c18 }, } ``` @@ -755,7 +754,7 @@ $4 = { = { <_objhdr> = { hub = 0x1dc860, - idHash = 550750288 + idHash = 1530752816 }, }, members of java.lang.Class: name = 0x171af8, @@ -768,4 +767,24 @@ Since the indirect types inherit from the corresponding raw type it is possible to use an expression that identifies an indirect type pointer in almost all cases where an expression identifying a raw type pointer would work. The only case case where care might be needed is when -casting a displayed numeric field value or displayed register value. \ No newline at end of file +casting a displayed numeric field value or displayed register value. + +For example, if the indirect hub oop printed above is passed to +hubname_raw the cast to type Object internal to that command fails to +force the required indirect oop translation and the resulting memory +access fails: + +``` +(gdb) hubname_raw 0x1dc860 +Cannot access memory at address 0x1dc860 +``` + +In this case it is necessary to use a slightly different command that +casts its argument to an indirect pointer type: +``` +(gdb) define hubname_indirect + x/s (('_z_.java.lang.Object' *)($arg0))->hub->name->value->data +end +(gdb) hubname_indirect 0x1dc860 +0x7ffff78a52f0: "java.lang.Class" +``` diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ArrayTypeEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ArrayTypeEntry.java index 080464d2ca49..3b2d3b75128e 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ArrayTypeEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ArrayTypeEntry.java @@ -31,9 +31,9 @@ import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugTypeInfo.DebugTypeKind; import org.graalvm.compiler.debug.DebugContext; -public class ArrayTypeEntry extends TypeEntry { +public class ArrayTypeEntry extends StructureTypeEntry { private TypeEntry elementType; - private int headerSize; + private int baseSize; private int lengthOffset; public ArrayTypeEntry(String typeName, int size) { @@ -50,9 +50,11 @@ public void addDebugInfo(DebugInfoBase debugInfoBase, DebugTypeInfo debugTypeInf DebugArrayTypeInfo debugArrayTypeInfo = (DebugArrayTypeInfo) debugTypeInfo; String elementTypeName = TypeEntry.canonicalize(debugArrayTypeInfo.elementType()); this.elementType = debugInfoBase.lookupTypeEntry(elementTypeName); - this.headerSize = debugArrayTypeInfo.headerSize(); + this.baseSize = debugArrayTypeInfo.baseSize(); this.lengthOffset = debugArrayTypeInfo.lengthOffset(); - debugContext.log("typename %s element type %s header size %d length offset %d\n", typeName, elementTypeName, headerSize, lengthOffset); + /* Add details of fields and field types */ + debugArrayTypeInfo.fieldInfoProvider().forEach(debugFieldInfo -> this.processField(debugFieldInfo, debugInfoBase, debugContext)); + debugContext.log("typename %s element type %s base size %d length offset %d\n", typeName, elementTypeName, baseSize, lengthOffset); } public TypeEntry getElementType() { 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 48abf55bf4ba..0ba310d86e6e 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 @@ -26,6 +26,7 @@ package com.oracle.objectfile.debugentry; +import com.oracle.objectfile.debuginfo.DebugInfoProvider; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugFrameSizeChange; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugMethodInfo; import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugTypeInfo; @@ -188,6 +189,10 @@ public int localDirsIdx(DirEntry dirEntry) { } } + public int localFilesIdx() { + return localFilesIndex.get(fileEntry); + } + public int localFilesIdx(@SuppressWarnings("hiding") FileEntry fileEntry) { return localFilesIndex.get(fileEntry); } @@ -290,6 +295,16 @@ protected void processMethod(DebugMethodInfo debugMethodInfo, DebugInfoBase debu methods.add(new MethodEntry(methodFileEntry, methodName, this, resultType, paramTypeArray, paramNameArray, modifiers)); } + @Override + protected FieldEntry addField(DebugInfoProvider.DebugFieldInfo debugFieldInfo, DebugInfoBase debugInfoBase, DebugContext debugContext) { + FieldEntry fieldEntry = super.addField(debugFieldInfo, debugInfoBase, debugContext); + FileEntry fileEntry = fieldEntry.getFileEntry(); + if (fileEntry != null) { + indexLocalFileEntry(fileEntry); + } + return fieldEntry; + } + private static String formatParams(List paramTypes, List paramNames) { if (paramNames.size() == 0) { return ""; 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 6aad9bd27c5e..0630c3296931 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 @@ -115,23 +115,23 @@ public abstract class DebugInfoBase { */ private boolean useHeapBase; /** - * number of bits oops are left shifted by when using compressed oops + * Number of bits oops are left shifted by when using compressed oops. */ private int oopCompressShift; /** - * number of low order bits used for tagging oops + * Number of low order bits used for tagging oops. */ private int oopTagsCount; /** - * number of bytes used to store an oop reference + * Number of bytes used to store an oop reference. */ private int oopReferenceSize; /** - * alignment of object memory area (and, therefore, of any oop) in bytes + * Alignment of object memory area (and, therefore, of any oop) in bytes. */ private int oopAlignment; /** - * number of bits in oop which are guaranteed 0 by virtue of alignment + * Number of bits in oop which are guaranteed 0 by virtue of alignment. */ private int oopAlignShift; diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/StructureTypeEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/StructureTypeEntry.java index 2e69411c5f72..f097b19ca640 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/StructureTypeEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/StructureTypeEntry.java @@ -37,7 +37,7 @@ /** * An intermediate type that provides behaviour for managing fields. This unifies code for handling - * header structures and Java instance classes that both support data members. + * header structures and Java instance and array classes that both support data members. */ public abstract class StructureTypeEntry extends TypeEntry { /** @@ -55,6 +55,11 @@ public Stream fields() { } protected void processField(DebugFieldInfo debugFieldInfo, DebugInfoBase debugInfoBase, DebugContext debugContext) { + /* Delegate this so superclasses can override this and inspect the computed FieldEntry. */ + addField(debugFieldInfo, debugInfoBase, debugContext); + } + + protected FieldEntry addField(DebugFieldInfo debugFieldInfo, DebugInfoBase debugInfoBase, DebugContext debugContext) { String fieldName = debugInfoBase.uniqueDebugString(debugFieldInfo.name()); String valueTypeName = TypeEntry.canonicalize(debugFieldInfo.valueType()); int fieldSize = debugFieldInfo.size(); @@ -69,7 +74,9 @@ protected void processField(DebugFieldInfo debugFieldInfo, DebugInfoBase debugIn // n.b. the field file may differ from the owning class file when the field is a // substitution FileEntry fileEntry = debugInfoBase.ensureFileEntry(fileName, filePath, cachePath); - fields.add(new FieldEntry(fileEntry, fieldName, this, valueType, fieldSize, fieldoffset, fieldModifiers)); + FieldEntry fieldEntry = new FieldEntry(fileEntry, fieldName, this, valueType, fieldSize, fieldoffset, fieldModifiers); + fields.add(fieldEntry); + return fieldEntry; } String memberModifiers(int modifiers) { 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 d5e4a8d8120b..b2e3146966a1 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 @@ -143,11 +143,13 @@ interface DebugInterfaceTypeInfo extends DebugInstanceTypeInfo { } interface DebugArrayTypeInfo extends DebugTypeInfo { - int headerSize(); + int baseSize(); int lengthOffset(); String elementType(); + + Stream fieldInfoProvider(); } interface DebugPrimitiveTypeInfo extends DebugTypeInfo { 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 0c23554929f8..4a561450fc4a 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 @@ -1032,7 +1032,7 @@ private int writeArrayLayoutAbbrev(@SuppressWarnings("unused") DebugContext cont int pos = p; pos = writeAbbrevCode(DwarfDebugInfo.DW_ABBREV_CODE_array_layout, buffer, pos); - pos = writeTag(DwarfDebugInfo.DW_TAG_structure_type, buffer, pos); + pos = writeTag(DwarfDebugInfo.DW_TAG_class_type, buffer, pos); pos = writeFlag(DwarfDebugInfo.DW_CHILDREN_yes, buffer, pos); pos = writeAttrType(DwarfDebugInfo.DW_AT_name, buffer, pos); pos = writeAttrForm(DwarfDebugInfo.DW_FORM_strp, 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 17fe7eaa982a..3a08f4bba203 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfDebugInfo.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfDebugInfo.java @@ -32,6 +32,7 @@ import com.oracle.objectfile.debugentry.ClassEntry; import com.oracle.objectfile.debugentry.DebugInfoBase; +import com.oracle.objectfile.debugentry.StructureTypeEntry; import com.oracle.objectfile.debugentry.TypeEntry; import com.oracle.objectfile.elf.ELFMachine; @@ -432,8 +433,8 @@ static class DwarfClassProperties extends DwarfTypeProperties { */ private HashMap methodDeclarationIndex; - DwarfClassProperties(ClassEntry classEntry) { - super(classEntry); + DwarfClassProperties(StructureTypeEntry entry) { + super(entry); this.cuIndex = -1; this.deoptCUIndex = -1; this.layoutIndex = -1; @@ -456,10 +457,10 @@ private DwarfTypeProperties addTypeProperties(TypeEntry typeEntry) { return typeProperties; } - private DwarfClassProperties addClassProperties(ClassEntry classEntry) { - String typeName = classEntry.getTypeName(); + private DwarfClassProperties addClassProperties(StructureTypeEntry entry) { + String typeName = entry.getTypeName(); assert propertiesIndex.get(typeName) == null; - DwarfClassProperties classProperties = new DwarfClassProperties(classEntry); + DwarfClassProperties classProperties = new DwarfClassProperties(entry); this.propertiesIndex.put(typeName, classProperties); return classProperties; } @@ -477,13 +478,13 @@ private DwarfTypeProperties lookupTypeProperties(TypeEntry typeEntry) { } } - private DwarfClassProperties lookupClassProperties(ClassEntry classEntry) { - String typeName = classEntry.getTypeName(); + private DwarfClassProperties lookupClassProperties(StructureTypeEntry entry) { + String typeName = entry.getTypeName(); DwarfTypeProperties typeProperties = propertiesIndex.get(typeName); assert typeProperties == null || typeProperties instanceof DwarfClassProperties; DwarfClassProperties classProperties = (DwarfClassProperties) typeProperties; if (classProperties == null) { - classProperties = addClassProperties(classEntry); + classProperties = addClassProperties(entry); } return classProperties; } @@ -646,10 +647,10 @@ public int getLineSectionSize(ClassEntry classEntry) { return classProperties.lineSectionSize; } - public void setFieldDeclarationIndex(ClassEntry classEntry, String fieldName, int pos) { + public void setFieldDeclarationIndex(StructureTypeEntry entry, String fieldName, int pos) { DwarfClassProperties classProperties; - classProperties = lookupClassProperties(classEntry); - assert classProperties.getTypeEntry() == classEntry; + classProperties = lookupClassProperties(entry); + assert classProperties.getTypeEntry() == entry; HashMap fieldDeclarationIndex = classProperties.fieldDeclarationIndex; if (fieldDeclarationIndex == null) { classProperties.fieldDeclarationIndex = fieldDeclarationIndex = new HashMap<>(); @@ -661,10 +662,10 @@ public void setFieldDeclarationIndex(ClassEntry classEntry, String fieldName, in } } - public int getFieldDeclarationIndex(ClassEntry classEntry, String fieldName) { + public int getFieldDeclarationIndex(StructureTypeEntry entry, String fieldName) { DwarfClassProperties classProperties; - classProperties = lookupClassProperties(classEntry); - assert classProperties.getTypeEntry() == classEntry; + classProperties = lookupClassProperties(entry); + assert classProperties.getTypeEntry() == entry; HashMap fieldDeclarationIndex = classProperties.fieldDeclarationIndex; assert fieldDeclarationIndex != null; assert fieldDeclarationIndex.get(fieldName) != null; 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 c3ac6ec863bc..0f1793d79b7d 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 @@ -60,11 +60,6 @@ public class DwarfInfoSectionImpl extends DwarfSectionImpl { */ public static final String OBJECT_HEADER_STRUCT_NAME = "_objhdr"; - /** - * The name of a special DWARF struct type used to model an array header. - */ - public static final String ARRAY_HEADER_STRUCT_NAME = "_arrhdr"; - /** * An info header section always contains a fixed number of bytes. */ @@ -512,7 +507,7 @@ private int writeClassLayout(DebugContext context, ClassEntry classEntry, byte[] int size = classEntry.getSize(); log(context, " [0x%08x] byte_size 0x%x", pos, size); pos = writeAttrData2((short) size, buffer, pos); - int fileIdx = classEntry.localFilesIdx(classEntry.getFileEntry()); + int fileIdx = classEntry.localFilesIdx(); log(context, " [0x%08x] file 0x%x (%s)", pos, fileIdx, classEntry.getFileName()); pos = writeAttrData2((short) fileIdx, buffer, pos); if (abbrevCode == DwarfDebugInfo.DW_ABBREV_CODE_class_layout2) { @@ -598,10 +593,10 @@ private static boolean isManifestedField(FieldEntry fieldEntry) { return fieldEntry.getOffset() >= 0; } - private int writeField(DebugContext context, ClassEntry classEntry, FieldEntry fieldEntry, byte[] buffer, int p) { + private int writeField(DebugContext context, StructureTypeEntry entry, FieldEntry fieldEntry, byte[] buffer, int p) { int pos = p; int modifiers = fieldEntry.getModifiers(); - boolean hasFile = classEntry.getFileName().length() > 0; + boolean hasFile = fieldEntry.getFileName().length() > 0; log(context, " [0x%08x] field definition", pos); int abbrevCode; boolean isStatic = Modifier.isStatic(modifiers); @@ -618,7 +613,7 @@ private int writeField(DebugContext context, ClassEntry classEntry, FieldEntry f abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_field_declaration4; } /* Record the position of the declaration to use when we write the definition. */ - setFieldDeclarationIndex(classEntry, fieldEntry.fieldName(), pos); + setFieldDeclarationIndex(entry, fieldEntry.fieldName(), pos); } log(context, " [0x%08x] <2> Abbrev Number %d", pos, abbrevCode); pos = writeAbbrevCode(abbrevCode, buffer, pos); @@ -628,8 +623,10 @@ private int writeField(DebugContext context, ClassEntry classEntry, FieldEntry f pos = writeAttrStrp(name, buffer, pos); /* We may not have a file and line for a field. */ if (hasFile) { - int fileIdx = classEntry.localFilesIdx(classEntry.getFileEntry()); - log(context, " [0x%08x] filename 0x%x (%s)", pos, fileIdx, classEntry.getFileName()); + assert entry instanceof ClassEntry; + int fileIdx = ((ClassEntry) entry).localFilesIdx(fieldEntry.getFileEntry()); + assert fileIdx > 0; + log(context, " [0x%08x] filename 0x%x (%s)", pos, fileIdx, fieldEntry.getFileName()); pos = writeAttrData2((short) fileIdx, buffer, pos); /* At present we definitely don't have line numbers. */ } @@ -685,7 +682,7 @@ private int writeMethodDeclaration(DebugContext context, ClassEntry classEntry, String name = uniqueDebugString(range.getMethodName()); log(context, " [0x%08x] name 0x%x (%s)", pos, debugStringIndex(name), name); pos = writeAttrStrp(name, buffer, pos); - int fileIdx = classEntry.localFilesIdx(range.getFileEntry()); + int fileIdx = classEntry.localFilesIdx(); log(context, " [0x%08x] file 0x%x (%s)", pos, fileIdx, range.getFileEntry().getFullName()); pos = writeAttrData2((short) fileIdx, buffer, pos); String returnTypeName = range.getMethodReturnTypeName(); @@ -1020,14 +1017,7 @@ private int writeArrayTypeUnit(DebugContext context, ArrayTypeEntry arrayTypeEnt pos = writeAttrStrp(name, buffer, pos); /* Write the array layout and array reference DIEs. */ TypeEntry elementType = arrayTypeEntry.getElementType(); - StructureTypeEntry headerType; - if (elementType.isPrimitive()) { - PrimitiveTypeEntry primitiveTypeEntry = (PrimitiveTypeEntry) elementType; - headerType = (StructureTypeEntry) lookupType(ARRAY_HEADER_STRUCT_NAME + primitiveTypeEntry.getTypeChar()); - } else { - headerType = (StructureTypeEntry) lookupType(ARRAY_HEADER_STRUCT_NAME + "A"); - } - int size = headerType.getSize(); + int size = arrayTypeEntry.getSize(); int layoutIdx = pos; pos = writeArrayLayout(context, arrayTypeEntry, elementType, size, buffer, pos); int indirectLayoutIdx = pos; @@ -1063,6 +1053,7 @@ private int writeArrayLayout(DebugContext context, ArrayTypeEntry arrayTypeEntry /* write a type definition for the element array field. */ int arrayDataTypeIdx = pos; pos = writeArrayDataType(context, elementType, buffer, pos); + pos = writeFields(context, arrayTypeEntry, buffer, pos); /* Write a zero length element array field. */ pos = writeArrayElementField(context, size, arrayDataTypeIdx, buffer, pos); pos = writeArraySuperReference(context, arrayTypeEntry, buffer, pos); @@ -1072,6 +1063,11 @@ private int writeArrayLayout(DebugContext context, ArrayTypeEntry arrayTypeEntry return writeAttrNull(buffer, pos); } + private int writeFields(DebugContext context, ArrayTypeEntry arrayTypeEntry, byte[] buffer, int p) { + return arrayTypeEntry.fields().filter(DwarfInfoSectionImpl::isManifestedField).reduce(p, + (pos, fieldEntry) -> writeField(context, arrayTypeEntry, fieldEntry, buffer, pos), + (oldPos, newPos) -> newPos); + } private int writeIndirectArrayLayout(DebugContext context, ArrayTypeEntry arrayTypeEntry, int size, int layoutOffset, byte[] buffer, int p) { int pos = p; @@ -1139,20 +1135,17 @@ private int writeArrayElementField(DebugContext context, int offset, int arrayDa private int writeArraySuperReference(DebugContext context, ArrayTypeEntry arrayTypeEntry, byte[] buffer, int p) { int pos = p; - String headerName; - TypeEntry elementType = arrayTypeEntry.getElementType(); - if (elementType.isPrimitive()) { - headerName = ARRAY_HEADER_STRUCT_NAME + ((PrimitiveTypeEntry) elementType).getTypeChar(); - } else { - headerName = ARRAY_HEADER_STRUCT_NAME + "A"; - } - int headerTypeOffset = getTypeIndex(headerName); + /* Arrays all inherit from java.lang.Object */ + String superName = "java.lang.Object"; + TypeEntry objectType = lookupType(superName); + assert objectType instanceof ClassEntry; + int superOffset = getLayoutIndex((ClassEntry) objectType); log(context, " [0x%08x] super reference", pos); int abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_super_reference; log(context, " [0x%08x] <2> Abbrev Number %d", pos, abbrevCode); pos = writeAbbrevCode(abbrevCode, buffer, pos); - log(context, " [0x%08x] type 0x%x (%s)", pos, headerTypeOffset, headerName); - pos = writeAttrRefAddr(headerTypeOffset, buffer, pos); + log(context, " [0x%08x] type 0x%x (%s)", pos, superOffset, superName); + pos = writeAttrRefAddr(superOffset, buffer, pos); /* Parent layout is embedded at start of object. */ log(context, " [0x%08x] data_member_location (super) 0x%x", pos, 0); pos = writeAttrData1((byte) 0, 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 444d0ad4545f..bfc14cbd7d71 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 @@ -432,7 +432,7 @@ private int writeLineNumberTable(DebugContext context, ClassEntry classEntry, by /* * The primary file entry should always be first in the local files list. */ - assert classEntry.localFilesIdx(fileEntry) == 1; + assert classEntry.localFilesIdx() == 1; String primaryClassName = classEntry.getTypeName(); String primaryFileName = classEntry.getFileName(); String file = primaryFileName; @@ -516,6 +516,7 @@ private int writeLineNumberTable(DebugContext context, ClassEntry classEntry, by } String subfile = subFileEntry.getFileName(); int subFileIdx = classEntry.localFilesIdx(subFileEntry); + assert subFileIdx > 0; long subLine = subrange.getLine(); long subAddressLo = subrange.getLo(); long subAddressHi = subrange.getHi(); 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 6594a571b98c..c2aead878d39 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfSectionImpl.java @@ -32,6 +32,7 @@ import com.oracle.objectfile.LayoutDecisionMap; import com.oracle.objectfile.ObjectFile; import com.oracle.objectfile.debugentry.ClassEntry; +import com.oracle.objectfile.debugentry.StructureTypeEntry; import com.oracle.objectfile.debugentry.TypeEntry; import com.oracle.objectfile.elf.ELFMachine; import com.oracle.objectfile.elf.ELFObjectFile; @@ -570,15 +571,15 @@ protected void setLayoutIndex(ClassEntry classEntry, int pos) { dwarfSections.setLayoutIndex(classEntry, pos); } - protected void setFieldDeclarationIndex(ClassEntry classEntry, String fieldName, int pos) { - dwarfSections.setFieldDeclarationIndex(classEntry, fieldName, pos); + protected void setFieldDeclarationIndex(StructureTypeEntry entry, String fieldName, int pos) { + dwarfSections.setFieldDeclarationIndex(entry, fieldName, pos); } - protected int getFieldDeclarationIndex(ClassEntry classEntry, String fieldName) { + protected int getFieldDeclarationIndex(StructureTypeEntry entry, String fieldName) { if (!contentByteArrayCreated()) { return 0; } - return dwarfSections.getFieldDeclarationIndex(classEntry, fieldName); + return dwarfSections.getFieldDeclarationIndex(entry, fieldName); } protected void setMethodDeclarationIndex(ClassEntry classEntry, String methodName, int pos) { 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 ff6c6709e40f..eca6fa8ddff3 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 @@ -420,68 +420,68 @@ public int size() { public Stream fieldInfoProvider() { return fieldInfos.stream(); } + } - 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) { - this.name = name; - this.ownerType = ownerType; - this.valueType = valueType; - this.offset = offset; - this.size = size; - this.modifiers = Modifier.PUBLIC; - } + 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) { + this.name = name; + this.ownerType = ownerType; + this.valueType = valueType; + this.offset = offset; + this.size = size; + this.modifiers = Modifier.PUBLIC; + } - @Override - public String name() { - return name; - } + @Override + public String name() { + return name; + } - @Override - public String ownerType() { - return ownerType; - } + @Override + public String ownerType() { + return ownerType; + } - @Override - public String valueType() { - return valueType; - } + @Override + public String valueType() { + return valueType; + } - @Override - public int offset() { - return offset; - } + @Override + public int offset() { + return offset; + } - @Override - public int size() { - return size; - } + @Override + public int size() { + return size; + } - @Override - public int modifiers() { - return modifiers; - } + @Override + public int modifiers() { + return modifiers; + } - @Override - public String fileName() { - return ""; - } + @Override + public String fileName() { + return ""; + } - @Override - public Path filePath() { - return null; - } + @Override + public Path filePath() { + return null; + } - @Override - public Path cachePath() { - return null; - } + @Override + public Path cachePath() { + return null; } } @@ -491,11 +491,9 @@ private Stream computeHeaderTypeInfo() { int referenceSize = OBJECTLAYOUT.getReferenceSize(); int hubFieldSize = referenceSize; String hubTypeName = "java.lang.Class"; - int arrayLengthOffset = OBJECTLAYOUT.getArrayLengthOffset(); - int arrayLengthSize = OBJECTLAYOUT.sizeInBytes(JavaKind.Int); int idHashOffset = OBJECTLAYOUT.getIdentityHashCodeOffset(); int idHashSize = OBJECTLAYOUT.sizeInBytes(JavaKind.Int); - int objHeaderSize = OBJECTLAYOUT.getFirstFieldOffset(); + int objHeaderSize = OBJECTLAYOUT.getMinimumInstanceObjectSize(); /* We need array headers for all Java kinds */ @@ -507,6 +505,7 @@ private Stream computeHeaderTypeInfo() { infos.add(objHeader); /* Create a header for each array type. */ + /* for (JavaKind arrayKind : ARRAY_KINDS) { String name = "_arrhdr" + arrayKind.getTypeChar(); int headerSize = OBJECTLAYOUT.getArrayBaseOffset(arrayKind); @@ -518,6 +517,7 @@ private Stream computeHeaderTypeInfo() { arrHeader.addField("len", "int", arrayLengthOffset, arrayLengthSize); infos.add(arrHeader); } + */ return infos.stream(); } @@ -730,10 +730,24 @@ public DebugTypeKind typeKind() { private class NativeImageDebugArrayTypeInfo extends NativeImageDebugTypeInfo implements DebugArrayTypeInfo { HostedArrayClass arrayClass; + List fieldInfos; NativeImageDebugArrayTypeInfo(HostedArrayClass arrayClass) { super(arrayClass); this.arrayClass = arrayClass; + this.fieldInfos = new LinkedList<>(); + JavaKind arrayKind = arrayClass.getBaseType().getJavaKind(); + int headerSize = OBJECTLAYOUT.getArrayBaseOffset(arrayKind); + int arrayLengthOffset = OBJECTLAYOUT.getArrayLengthOffset(); + int arrayLengthSize = OBJECTLAYOUT.sizeInBytes(JavaKind.Int); + assert arrayLengthOffset + arrayLengthSize <= headerSize; + + addField("len", "int", arrayLengthOffset, arrayLengthSize); + } + + void addField(String name, String valueType, int offset, @SuppressWarnings("hiding") int size) { + NativeImageDebugHeaderFieldInfo fieldinfo = new NativeImageDebugHeaderFieldInfo(name, typeName(), valueType, offset, size); + fieldInfos.add(fieldinfo); } @Override @@ -742,7 +756,7 @@ public DebugTypeKind typeKind() { } @Override - public int headerSize() { + public int baseSize() { return OBJECTLAYOUT.getArrayBaseOffset(arrayClass.getComponentType().getStorageKind()); } @@ -756,6 +770,11 @@ public String elementType() { HostedType elementType = arrayClass.getComponentType(); return toJavaName(elementType); } + + @Override + public Stream fieldInfoProvider() { + return fieldInfos.stream(); + } } private class NativeImageDebugPrimitiveTypeInfo extends NativeImageDebugTypeInfo implements DebugPrimitiveTypeInfo { From 2c3c8abf4c59df5492f9add84f8d5b442c56825d Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Mon, 8 Feb 2021 11:37:00 +0000 Subject: [PATCH 6/6] Correct debug info test script for new object layout and to bypass latest gdb bug minor code cleanups style fixes fix comment text further fixes after latest review --- substratevm/DebugInfo.md | 2 +- substratevm/mx.substratevm/testhello.py | 35 +++++++----- .../objectfile/debugentry/ClassEntry.java | 20 ++++--- .../objectfile/debugentry/DebugInfoBase.java | 17 +++--- .../debugentry/InterfaceClassEntry.java | 6 +- .../debugentry/StructureTypeEntry.java | 6 +- .../debuginfo/DebugInfoProvider.java | 8 +-- .../elf/dwarf/DwarfAbbrevSectionImpl.java | 10 ++-- .../objectfile/elf/dwarf/DwarfDebugInfo.java | 13 +++-- .../elf/dwarf/DwarfInfoSectionImpl.java | 57 ++----------------- .../image/NativeImageDebugInfoProvider.java | 51 ++++++----------- 11 files changed, 88 insertions(+), 137 deletions(-) diff --git a/substratevm/DebugInfo.md b/substratevm/DebugInfo.md index 987f1d94bd08..784d44a936c0 100644 --- a/substratevm/DebugInfo.md +++ b/substratevm/DebugInfo.md @@ -106,7 +106,7 @@ compiled method. ### Currently Missing Features - - reference by name to values boudn to parameter and local vars + - reference by name to values bound to parameter and local vars This feature is scheduled for inclusion in a later release. diff --git a/substratevm/mx.substratevm/testhello.py b/substratevm/mx.substratevm/testhello.py index 155f36bca9e7..d63d2d677f1f 100644 --- a/substratevm/mx.substratevm/testhello.py +++ b/substratevm/mx.substratevm/testhello.py @@ -42,6 +42,7 @@ import re import sys +import os # A helper class which checks that a sequence of lines of output # from a gdb command matches a sequence of per-line regular @@ -139,10 +140,16 @@ def test(): # n.b. can only get back here with one match match = matches[0] major = int(match.group(1)) - # may need this if 8.x and 9.x get patched - # minor = int(match.group(2)) - # printing object data requires gdb 10+ - can_print_data = major > 9 + minor = int(match.group(2)) + # printing object data requires a patched gdb + # once the patch is in we can check for a suitable + # range of major.minor versions + # for now we use an env setting + print("Found gdb version %s.%s"%(major, minor)) + # can_print_data = major > 10 or (major == 10 and minor > 1) + can_print_data = False + if os.environ.get('GDB_CAN_PRINT', '') == 'True': + can_print_data = True if not can_print_data: print("Warning: cannot test printing of objects!") @@ -182,12 +189,13 @@ def test(): exec_string = execute("print /x *(('java.lang.String[]' *)$rdi)") checker = Checker("print String[] args", [r"%s = {"%(wildcard_pattern), - r"%s<_arrhdrA> = {"%(spaces_pattern), + r"%s = {"%(spaces_pattern), + r"%s<_objhdr> = {"%(spaces_pattern), r"%shub = %s,"%(spaces_pattern, address_pattern), - r"%sidHash = %s,"%(spaces_pattern, address_pattern), - r"%slen = 0x0"%(spaces_pattern), - r"%s},"%(spaces_pattern), + r"%sidHash = %s"%(spaces_pattern, address_pattern), + r"%s}, }, "%(spaces_pattern), r"%smembers of java\.lang\.String\[\]:"%(spaces_pattern), + r"%slen = 0x0,"%(spaces_pattern), r"%sdata = %s"%(spaces_pattern, address_pattern), "}"]) @@ -225,7 +233,7 @@ def test(): # ensure we can dereference static fields exec_string = execute("print 'java.math.BigDecimal'::BIG_TEN_POWERS_TABLE->data[3]->mag->data[0]") checker = Checker("print static field value contents", - r"%s = 1000\$"%(wildcard_pattern)) + r"%s = 1000"%(wildcard_pattern)) checker.check(exec_string, skip_fails=False) # look up PrintStream.println methods @@ -308,13 +316,12 @@ def test(): checker = Checker('ptype _objhdr', rexp) checker.check(exec_string, skip_fails=True) - exec_string = execute("ptype _arrhdrA") - rexp = [r"type = struct _arrhdrA {", - r"%sjava\.lang\.Class \*hub;"%(spaces_pattern), - r"%sint idHash;"%(spaces_pattern), + exec_string = execute("ptype 'java.lang.String[]'") + rexp = [r"type = class java.lang.String\[\] : public java.lang.Object {", r"%sint len;"%(spaces_pattern), + r"%sjava\.lang\.String \*data\[0\];"%(spaces_pattern), r"}"] - checker = Checker('ptype _objhdr', rexp) + checker = Checker('ptype String[]', rexp) checker.check(exec_string, skip_fails=True) # run a backtrace 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 0ba310d86e6e..7dc4e567b0df 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 @@ -74,7 +74,7 @@ public class ClassEntry extends StructureTypeEntry { */ private Map localFilesIndex; /** - * a list of the same files. + * A list of the same files. */ private LinkedList localFiles; /** @@ -86,7 +86,7 @@ public class ClassEntry extends StructureTypeEntry { */ private LinkedList localDirs; /** - * true iff the entry includes methods that are deopt targets. + * This flag is true iff the entry includes methods that are deopt targets. */ private boolean includesDeoptTarget; @@ -289,8 +289,10 @@ protected void processMethod(DebugMethodInfo debugMethodInfo, DebugInfoBase debu String fileName = debugMethodInfo.fileName(); Path filePath = debugMethodInfo.filePath(); Path cachePath = debugMethodInfo.cachePath(); - // n.b. the method file may differ from the owning class file when the method is a - // substitution + /* + * n.b. the method file may differ from the owning class file when the method is a + * substitution + */ FileEntry methodFileEntry = debugInfoBase.ensureFileEntry(fileName, filePath, cachePath); methods.add(new MethodEntry(methodFileEntry, methodName, this, resultType, paramTypeArray, paramNameArray, modifiers)); } @@ -298,9 +300,9 @@ protected void processMethod(DebugMethodInfo debugMethodInfo, DebugInfoBase debu @Override protected FieldEntry addField(DebugInfoProvider.DebugFieldInfo debugFieldInfo, DebugInfoBase debugInfoBase, DebugContext debugContext) { FieldEntry fieldEntry = super.addField(debugFieldInfo, debugInfoBase, debugContext); - FileEntry fileEntry = fieldEntry.getFileEntry(); - if (fileEntry != null) { - indexLocalFileEntry(fileEntry); + FileEntry fieldFileEntry = fieldEntry.getFileEntry(); + if (fieldFileEntry != null) { + indexLocalFileEntry(fieldFileEntry); } return fieldEntry; } @@ -344,13 +346,13 @@ public Range makePrimaryRange(String methodName, String symbolName, String param */ for (MethodEntry methodEntry : methods) { if (methodEntry.match(methodName, paramSignature, returnTypeName)) { - // maybe the method's file entry + /* maybe the method's file entry */ fileEntryToUse = methodEntry.getFileEntry(); break; } } if (fileEntryToUse == null) { - /* last chance is the class's file entry */ + /* Last chance is the class's file entry. */ fileEntryToUse = this.fileEntry; } } 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 0630c3296931..72a4bea42e23 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 @@ -160,18 +160,18 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { */ /* - * track whether we need to use a heap base regsiter + * Track whether we need to use a heap base register. */ useHeapBase = debugInfoProvider.useHeapBase(); /* - * save count of low order tag bits that may appear in references + * Save count of low order tag bits that may appear in references. */ int oopTagsMask = debugInfoProvider.oopTagsMask(); - /* tag bits must be between 1 and 32 for us to emit as DW_OP_lit */ + /* Tag bits must be between 1 and 32 for us to emit as DW_OP_lit. */ assert oopTagsMask > 0 && oopTagsMask < 32; - /* mask must be contiguous from bit 0 */ + /* Mask must be contiguous from bit 0. */ assert ((oopTagsMask + 1) & oopTagsMask) == 0; oopTagsCount = Integer.bitCount(oopTagsMask); @@ -185,13 +185,13 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { /* Save number of bytes in a reference field. */ oopReferenceSize = debugInfoProvider.oopReferenceSize(); - /* Save alignment of a reference */ + /* Save alignment of a reference. */ oopAlignment = debugInfoProvider.oopAlignment(); - /* Save alignment of a reference */ + /* Save alignment of a reference. */ oopAlignShift = Integer.bitCount(oopAlignment - 1); - /* reference alignment must be 8 bytes */ + /* Reference alignment must be 8 bytes. */ assert oopAlignment == 8; /* Ensure we have a null string in the string section. */ @@ -499,7 +499,8 @@ public boolean isHubClassEntry(ClassEntry classEntry) { public int classLayoutAbbrevCode(ClassEntry classEntry) { if (useHeapBase & isHubClassEntry(classEntry)) { /* - * this layout adds special logic to remove tag bits from indirect pointers to this type + * This layout adds special logic to remove tag bits from indirect pointers to this + * type. */ return DwarfDebugInfo.DW_ABBREV_CODE_class_layout2; } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/InterfaceClassEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/InterfaceClassEntry.java index df204bd0c4dd..9b929ca6332d 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/InterfaceClassEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/InterfaceClassEntry.java @@ -74,10 +74,10 @@ public int getSize() { */ int maxSize = super.size; for (ClassEntry implementor : implementors) { - int size = implementor.getSize(); + int nextSize = implementor.getSize(); - if (size > maxSize) { - maxSize = size; + if (nextSize > maxSize) { + maxSize = nextSize; } } return maxSize; diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/StructureTypeEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/StructureTypeEntry.java index f097b19ca640..dcc80a0eb71f 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/StructureTypeEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/StructureTypeEntry.java @@ -71,8 +71,10 @@ protected FieldEntry addField(DebugFieldInfo debugFieldInfo, DebugInfoBase debug String fileName = debugFieldInfo.fileName(); Path filePath = debugFieldInfo.filePath(); Path cachePath = debugFieldInfo.cachePath(); - // n.b. the field file may differ from the owning class file when the field is a - // substitution + /* + * n.b. the field file may differ from the owning class file when the field is a + * substitution + */ FileEntry fileEntry = debugInfoBase.ensureFileEntry(fileName, filePath, cachePath); FieldEntry fieldEntry = new FieldEntry(fileEntry, fieldName, this, valueType, fieldSize, fieldoffset, fieldModifiers); fields.add(fieldEntry); 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 b2e3146966a1..45eb6ab01002 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 @@ -41,22 +41,22 @@ public interface DebugInfoProvider { boolean useHeapBase(); /** - * number of bits oops are left shifted by when using compressed oops + * Number of bits oops are left shifted by when using compressed oops. */ int oopCompressShift(); /** - * mask delecting low order bits used for tagging oops + * Mask delecting low order bits used for tagging oops. */ int oopTagsMask(); /** - * number of bytes used to store an oop reference + * Number of bytes used to store an oop reference. */ int oopReferenceSize(); /** - * alignment of object memory area (and, therefore, of any oop) in bytes + * Alignment of object memory area (and, therefore, of any oop) in bytes. */ int oopAlignment(); 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 4a561450fc4a..da7deea67f56 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 @@ -948,7 +948,7 @@ private int writeMethodDeclarationAbbrev(@SuppressWarnings("unused") DebugContex /* We don't (yet?) have a proper start line for the method itself */ // pos = writeAttrType(DwarfDebugInfo.DW_AT_decl_line, buffer, pos); // pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data2, buffer, pos); - /* This probably needs to use the symbol name -- n.b. it is not in DWARF2 */ + /* This probably needs to use the symbol name */ // pos = writeAttrType(DwarfDebugInfo.DW_AT_linkage_name, buffer, pos); // pos = writeAttrForm(DwarfDebugInfo.DW_FORM_strp, buffer, pos); pos = writeAttrType(DwarfDebugInfo.DW_AT_type, buffer, pos); @@ -1187,7 +1187,7 @@ private int writeStaticFieldLocationAbbrev(@SuppressWarnings("unused") DebugCont pos = writeFlag(DwarfDebugInfo.DW_CHILDREN_no, buffer, pos); pos = writeAttrType(DwarfDebugInfo.DW_AT_specification, buffer, pos); pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref_addr, buffer, pos); - /* Do we have a symbol name to use here? n.b. this is not in DWARF2. */ + /* Do we have a symbol name to use here? */ // pos = writeAttrType(DwarfDebugInfo.DW_AT_linkage_name, buffer, pos); // pos = writeAttrForm(DwarfDebugInfo.DW_FORM_strp, buffer, pos); pos = writeAttrType(DwarfDebugInfo.DW_AT_location, buffer, pos); @@ -1209,11 +1209,9 @@ private int writeSuperReferenceAbbrev(@SuppressWarnings("unused") DebugContext c pos = writeAttrType(DwarfDebugInfo.DW_AT_type, buffer, pos); pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref_addr, buffer, pos); pos = writeAttrType(DwarfDebugInfo.DW_AT_data_member_location, buffer, pos); - pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data1, buffer, pos); // = offset? in which - // segment though? + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data1, buffer, pos); pos = writeAttrType(DwarfDebugInfo.DW_AT_accessibility, buffer, pos); - pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data1, buffer, pos); // = offset? in which - // segment though? + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data1, buffer, pos); /* * Now terminate. */ diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfDebugInfo.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfDebugInfo.java index 3a08f4bba203..301754c24b01 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 @@ -255,11 +255,14 @@ public class DwarfDebugInfo extends DebugInfoBase { public static final byte rthread_x86 = (byte) 15; /* - * prefix used to label indirect types used to ensure gdb performs oop reference --> raw address - * translation + * A prefix used to label indirect types used to ensure gdb performs oop reference --> raw + * address translation */ public static final String INDIRECT_PREFIX = "_z_."; - /* name of type for hub field which needs special case processing to remove tag bits */ + /* + * The name of the type for header field hub which needs special case processing to remove tag + * bits + */ public static final String HUB_TYPE_NAME = "java.lang.Class"; private DwarfStrSectionImpl dwarfStrSection; @@ -343,11 +346,11 @@ public byte getThreadRegister() { */ static class DwarfTypeProperties { /** - * index in debug_info section of type declaration for this class. + * Index in debug_info section of type declaration for this class. */ private int typeInfoIndex; /** - * index in debug_info section of indirect type declaration for this class. + * Index in debug_info section of indirect type declaration for this class. * * this is normally just the same as the index of the normal type declaration, however, when * oops are stored in static and instance fields as offsets from the heapbase register gdb 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 0f1793d79b7d..43144b7e5615 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 @@ -1056,7 +1056,7 @@ private int writeArrayLayout(DebugContext context, ArrayTypeEntry arrayTypeEntry pos = writeFields(context, arrayTypeEntry, buffer, pos); /* Write a zero length element array field. */ pos = writeArrayElementField(context, size, arrayDataTypeIdx, buffer, pos); - pos = writeArraySuperReference(context, arrayTypeEntry, buffer, pos); + pos = writeArraySuperReference(context, buffer, pos); /* * Write a terminating null attribute. */ @@ -1068,6 +1068,7 @@ private int writeFields(DebugContext context, ArrayTypeEntry arrayTypeEntry, byt (pos, fieldEntry) -> writeField(context, arrayTypeEntry, fieldEntry, buffer, pos), (oldPos, newPos) -> newPos); } + private int writeIndirectArrayLayout(DebugContext context, ArrayTypeEntry arrayTypeEntry, int size, int layoutOffset, byte[] buffer, int p) { int pos = p; @@ -1133,26 +1134,14 @@ private int writeArrayElementField(DebugContext context, int offset, int arrayDa } - private int writeArraySuperReference(DebugContext context, ArrayTypeEntry arrayTypeEntry, byte[] buffer, int p) { + private int writeArraySuperReference(DebugContext context, byte[] buffer, int p) { int pos = p; /* Arrays all inherit from java.lang.Object */ String superName = "java.lang.Object"; TypeEntry objectType = lookupType(superName); assert objectType instanceof ClassEntry; int superOffset = getLayoutIndex((ClassEntry) objectType); - log(context, " [0x%08x] super reference", pos); - int abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_super_reference; - log(context, " [0x%08x] <2> Abbrev Number %d", pos, abbrevCode); - pos = writeAbbrevCode(abbrevCode, buffer, pos); - log(context, " [0x%08x] type 0x%x (%s)", pos, superOffset, superName); - pos = writeAttrRefAddr(superOffset, buffer, pos); - /* Parent layout is embedded at start of object. */ - log(context, " [0x%08x] data_member_location (super) 0x%x", pos, 0); - pos = writeAttrData1((byte) 0, buffer, pos); - log(context, " [0x%08x] modifiers public", pos); - int modifiers = Modifier.PUBLIC; - pos = writeAttrAccessibility(modifiers, buffer, pos); - return pos; + return writeSuperReference(context, superOffset, superName, buffer, pos); } private int writeArrayTypes(DebugContext context, ArrayTypeEntry arrayTypeEntry, int layoutOffset, int indirectLayoutOffset, byte[] buffer, int p) { @@ -1355,43 +1344,7 @@ public int writeAttrAccessibility(int modifiers, byte[] buffer, int p) { public int writeIndirectOopConversionExpression(boolean isHub, byte[] buffer, int p) { int pos = p; /* - * The conversion rules are different depending on whether they apply to the hub class or - * any other class. - * - * They also vary according to whether isolates are in use and, if so, whether it is - * combined with compression. - * - * Finally, they depend on the choice of GC, specifically it's influence on the number of GC - * tag bits. - * - * The rules are as follows: - * - * H:-SpawnIsolates (explicitly disabled isolates support) - * - *
      • Regular oops: address64 = val64
      • Oops pointing to hubs: address64 = val64 & - * "GC-bits bitmask"
      - * - * -H:+SpawnIsolates -H:-UseCompressedReferences (CE default) - * - *
      • Regular oops: address64 = val64 + r14
      • Oops pointing to hubs: address64 = - * ((val64 >> "num GC bits") << "objectAlignmentBits") + r14
      - * - * objectAlignmentBits should always be 3 - * - * -H:+SpawnIsolates+ -H:+UseCompressedReferences (EE default) - * - *
      • Regular oops: address64 = (val32 << "compressShift") + r14
      • Oops pointing - * to hubs: address64 = ((val32 >> "num GC bits") << "compressShift") + r14
      - * - * compressShift should always be 3. - * - * For Serial garbage collector (CE) - * - *
      • "num GC bits": 3
      • "GC-bits bitmask": ~0b111
      - * - * For G1 garbage collector (EE only) - * - *
      • "num GC bits": 5
      • "GC-bits bitmask": ~0b11111
      + * For an explanation of the conversion rules @see com.oracle.svm.core.heap.ReferenceAccess * * n.b. * 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 eca6fa8ddff3..3f2d65c32792 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 @@ -86,8 +86,6 @@ * to be passed to an ObjectFile when generation of debug info is enabled. */ class NativeImageDebugInfoProvider implements DebugInfoProvider { - private static final JavaKind[] ARRAY_KINDS = {JavaKind.Boolean, JavaKind.Byte, JavaKind.Char, JavaKind.Short, JavaKind.Int, JavaKind.Float, JavaKind.Long, JavaKind.Double, JavaKind.Object}; - private final DebugContext debugContext; private final NativeImageCodeCache codeCache; @SuppressWarnings("unused") private final NativeImageHeap heap; @@ -116,8 +114,8 @@ class NativeImageDebugInfoProvider implements DebugInfoProvider { this.useHeapBase = false; this.compressShift = 0; } - this.referenceSize = OBJECTLAYOUT.getReferenceSize(); - this.referenceAlignment = OBJECTLAYOUT.getAlignment(); + this.referenceSize = getObjectLayout().getReferenceSize(); + this.referenceAlignment = getObjectLayout().getAlignment(); /* Offsets need to be adjusted relative to the heap base plus partition-specific offset. */ primitiveStartOffset = (int) primitiveFields.getOffset(); referenceStartOffset = (int) objectFields.getOffset(); @@ -165,7 +163,9 @@ public Stream dataInfoProvider() { return heap.getObjects().stream().filter(this::acceptObjectInfo).map(this::createDebugDataInfo); } - static ObjectLayout OBJECTLAYOUT = ConfigurationValues.getObjectLayout(); + static ObjectLayout getObjectLayout() { + return ConfigurationValues.getObjectLayout(); + } /* * HostedType wraps an AnalysisType and both HostedType and AnalysisType punt calls to @@ -347,10 +347,10 @@ public int size() { return ((HostedInstanceClass) hostedType).getInstanceSize(); } else if (hostedType instanceof HostedArrayClass) { /* Use the size of header common to all arrays of this type. */ - return OBJECTLAYOUT.getArrayBaseOffset(hostedType.getComponentType().getStorageKind()); + return getObjectLayout().getArrayBaseOffset(hostedType.getComponentType().getStorageKind()); } else if (hostedType instanceof HostedInterface) { /* Use the size of the header common to all implementors. */ - return OBJECTLAYOUT.getFirstFieldOffset(); + return getObjectLayout().getFirstFieldOffset(); } else { /* Use the number of bytes needed needed to store the value. */ assert hostedType instanceof HostedPrimitiveType; @@ -487,13 +487,12 @@ public Path cachePath() { private Stream computeHeaderTypeInfo() { List infos = new LinkedList<>(); - int hubOffset = OBJECTLAYOUT.getHubOffset(); - int referenceSize = OBJECTLAYOUT.getReferenceSize(); + int hubOffset = getObjectLayout().getHubOffset(); int hubFieldSize = referenceSize; String hubTypeName = "java.lang.Class"; - int idHashOffset = OBJECTLAYOUT.getIdentityHashCodeOffset(); - int idHashSize = OBJECTLAYOUT.sizeInBytes(JavaKind.Int); - int objHeaderSize = OBJECTLAYOUT.getMinimumInstanceObjectSize(); + int idHashOffset = getObjectLayout().getIdentityHashCodeOffset(); + int idHashSize = getObjectLayout().sizeInBytes(JavaKind.Int); + int objHeaderSize = getObjectLayout().getMinimumInstanceObjectSize(); /* We need array headers for all Java kinds */ @@ -504,20 +503,6 @@ private Stream computeHeaderTypeInfo() { } infos.add(objHeader); - /* Create a header for each array type. */ - /* - for (JavaKind arrayKind : ARRAY_KINDS) { - String name = "_arrhdr" + arrayKind.getTypeChar(); - int headerSize = OBJECTLAYOUT.getArrayBaseOffset(arrayKind); - NativeImageHeaderTypeInfo arrHeader = new NativeImageHeaderTypeInfo(name, headerSize); - arrHeader.addField("hub", hubTypeName, hubOffset, hubFieldSize); - if (idHashOffset > 0) { - arrHeader.addField("idHash", "int", idHashOffset, idHashSize); - } - arrHeader.addField("len", "int", arrayLengthOffset, arrayLengthSize); - infos.add(arrHeader); - } - */ return infos.stream(); } @@ -545,7 +530,7 @@ public DebugTypeKind typeKind() { @Override public int headerSize() { - return OBJECTLAYOUT.getFirstFieldOffset(); + return getObjectLayout().getFirstFieldOffset(); } @Override @@ -638,7 +623,7 @@ public int offset() { @Override public int size() { - return OBJECTLAYOUT.sizeInBytes(field.getType().getStorageKind()); + return getObjectLayout().sizeInBytes(field.getType().getStorageKind()); } @Override @@ -737,9 +722,9 @@ private class NativeImageDebugArrayTypeInfo extends NativeImageDebugTypeInfo imp this.arrayClass = arrayClass; this.fieldInfos = new LinkedList<>(); JavaKind arrayKind = arrayClass.getBaseType().getJavaKind(); - int headerSize = OBJECTLAYOUT.getArrayBaseOffset(arrayKind); - int arrayLengthOffset = OBJECTLAYOUT.getArrayLengthOffset(); - int arrayLengthSize = OBJECTLAYOUT.sizeInBytes(JavaKind.Int); + int headerSize = getObjectLayout().getArrayBaseOffset(arrayKind); + int arrayLengthOffset = getObjectLayout().getArrayLengthOffset(); + int arrayLengthSize = getObjectLayout().sizeInBytes(JavaKind.Int); assert arrayLengthOffset + arrayLengthSize <= headerSize; addField("len", "int", arrayLengthOffset, arrayLengthSize); @@ -757,12 +742,12 @@ public DebugTypeKind typeKind() { @Override public int baseSize() { - return OBJECTLAYOUT.getArrayBaseOffset(arrayClass.getComponentType().getStorageKind()); + return getObjectLayout().getArrayBaseOffset(arrayClass.getComponentType().getStorageKind()); } @Override public int lengthOffset() { - return OBJECTLAYOUT.getArrayLengthOffset(); + return getObjectLayout().getArrayLengthOffset(); } @Override