diff --git a/substratevm/DebugInfo.md b/substratevm/DebugInfo.md index 0e69446d1c1a..784d44a936c0 100644 --- a/substratevm/DebugInfo.md +++ b/substratevm/DebugInfo.md @@ -12,6 +12,8 @@ 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. +## Source File Caching + 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 +43,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 +68,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,13 +80,12 @@ 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 _apps/target/greeter-sources.jar_. - ## Currently Implemented Features The currently implemented features include: @@ -90,12 +93,388 @@ 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. +### Currently Missing Features + + - reference by name to values bound 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 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 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 'java.lang.String' +type = class java.lang.String : public java.lang.Object { + private: + byte [] *value; + int hash; + byte coder; + + public: + 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 for a specific Java type. + +``` +(gdb) print *('java.lang.String' *) 0x7ffff7c01060 +$1 = { + = { + <_objhdr> = { + hub = 0x90cb58 + }, }, + members of java.lang.String: + value = 0x7ffff7c011a0, + hash = 0, + coder = 0 '\000' +} +``` + +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; + int idHash; +} + +(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 +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) +$2 = 0x7ffff7c01028 +(gdb) print *$2->hub->name->value +$3 = { + = { + <_objhdr> = { + hub = 0x942d40, + idHash = 1806863149 + }, }, + members of byte []: + len = 19, + data = 0x923a90 "[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 +$4 = { + = { + <_objhdr> = { + hub = 0x925be8, + idHash = 0 + }, }, + members of java.lang.String[]: + len = 1, + 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;" +``` + +Indeed it is useful to define a gdb command `hubname_raw` to execute this +operation on an arbitrary raw memory address + +``` +define hubname_raw + x/s (('java.lang.Object' *)($arg0))->hub->name->value->data +end + +(gdb) hubname_raw $rdi +0x904798: "[Ljava.lang.String;" +``` + +Attempting to print the hub name for an invalid reference will fail +safe, printing an error message. + +``` +(gdb) p/x $rdx +$5 = 0x2 +(gdb) hubname $rdx +Cannot access memory at address 0x2 +``` + +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 = class java.lang.String[] : public java.lang.Object { + int len; + java.lang.String *data[0]; +} +``` + +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. + +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 = class java.lang.String[] : public java.lang.Object { + int len; + java.lang.String *data[0]; +} + +define hubname + x/s (($arg0))->hub->name->value->data +end + +(gdb) hubname $4 +0x923b68: "[Ljava.lang.String;" +``` + +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.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; +} +``` + +Given a reference typed to an interface it can be resolved to the +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] +$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 $6 +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 $6->'_java.nio.CharBuffer' +0x7d96d8: "java.lang.String\270", +(gdb) print $6->'_java.lang.String' +$18 = { + = { + <_objhdr> = { + hub = 0x90cb58 + }, }, + members of java.lang.String: + value = 0x7ffff7c011a0, + hash = 0, + coder = 0 '\000' +} +``` + +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 +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 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) 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. +``` + + 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; + . . . + 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; +``` + +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 +$8 = (java.math.BigInteger[][] *) 0xa6fd98 +(gdb) p &'java.math.BigInteger'::powerCache +$9 = (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 +$10 = { + = { + <_objhdr> = { + hub = 0x9ab3d0, + idHash = 489620191 + }, }, + members of _java.math.BigInteger[][]: + len = 37, + data = 0xa6fda8 +} +(gdb) p 'java.math.BigInteger'::powerCache->data[0]@4 +$11 = {0x0, 0x0, 0xc09378, 0xc09360} +(gdb) p *'java.math.BigInteger'::powerCache->data[2] +$12 = { + = { + <_objhdr> = { + hub = 0x919898, + idHash = 1796421813 + }, }, + members of java.math.BigInteger[]: + len = 1, + data = 0xc09388 +} +(gdb) p *'java.math.BigInteger'::powerCache->data[2]->data[0] +$14 = { + = { + = { + <_objhdr> = { + hub = 0x919bc8 + }, }, }, + 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 @@ -269,3 +648,143 @@ 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 DWARF info record that defines it includes +information that tells gdb how to convert pointers to this type. + +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. + +``` +(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 = { + = { + = { + <_objhdr> = { + hub = 0x1ec0e2, + idHash = 0x2f462321 + }, }, + members of java.math.BigInteger[][]: + len = 0x25, + 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 = 1530752816 + }, }, + 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. + +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/mx.substratevm/mx_substratevm.py b/substratevm/mx.substratevm/mx_substratevm.py index 442fe9e2d0bd..ebffb1425e23 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 @@ -762,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 3b1e011018fc..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 @@ -130,11 +131,36 @@ 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)) + 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!") + # disable prompting to continue output execute("set pagination off") - # set a break point at 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") + 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 +175,88 @@ 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%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) + 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 = {"%(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\.String\[\]:"%(spaces_pattern), + r"%slen = 0x0,"%(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)(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 +268,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"%sint idHash;"%(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 '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 String[]', 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%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) # 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%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] @@ -224,11 +355,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%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 @@ -250,7 +381,40 @@ def test(): checker = Checker('list println 2', rexp) checker.check(exec_string, skip_fails=False) - # continue to next breakpoint + 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")) 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..3b2d3b75128e --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ArrayTypeEntry.java @@ -0,0 +1,63 @@ +/* + * 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 StructureTypeEntry { + private TypeEntry elementType; + private int baseSize; + 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()); + this.elementType = debugInfoBase.lookupTypeEntry(elementTypeName); + this.baseSize = debugArrayTypeInfo.baseSize(); + this.lengthOffset = debugArrayTypeInfo.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() { + 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..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 @@ -26,79 +26,75 @@ 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; +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; /** - * a list of the same files. + * A list of the same files. */ 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. + * This flag is 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 +110,34 @@ 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) { + @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); primaryEntries.add(primaryEntry); @@ -133,28 +148,32 @@ public void addPrimary(Range primary, List frameSizeInfos, /* deopt targets should all come after normal methods */ assert includesDeoptTarget == false; } + FileEntry primaryFileEntry = primary.getFileEntry(); + assert primaryFileEntry != null; + indexLocalFileEntry(primaryFileEntry); } } - public void addSubRange(Range subrange, FileEntry subFileEntry) { + 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, 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(); + indexLocalFileEntry(subFileEntry); + } + } + + 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()); @@ -170,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); } @@ -200,68 +223,15 @@ 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 int getDeoptCUIndex() { - // Should have been set before being read. - assert deoptCUIndex >= 0; - return deoptCUIndex; - } - - public int getLineIndex() { - return lineIndex; - } - - public void setLineIndex(int lineIndex) { - this.lineIndex = lineIndex; - } - - public void setLinePrologueSize(int linePrologueSize) { - this.linePrologueSize = linePrologueSize; - } - - public int getLinePrologueSize() { - return linePrologueSize; - } - - public int getTotalSize() { - return totalSize; - } - - public void setTotalSize(int totalSize) { - this.totalSize = totalSize; - } - public FileEntry getFileEntry() { return fileEntry; } - public String getClassName() { - return className; - } - public LinkedList getPrimaryEntries() { return primaryEntries; } + @SuppressWarnings("unused") public Object primaryIndexFor(Range primaryRange) { return primaryIndex.get(primaryRange); } @@ -280,9 +250,112 @@ public boolean includesDeoptTarget() { public String getCachePath() { if (fileEntry != null) { - return fileEntry.getCachePath(); - } else { + Path cachePath = fileEntry.getCachePath(); + if (cachePath != null) { + return cachePath.toString(); + } + } + return ""; + } + + 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); + } + + 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 methodFileEntry = debugInfoBase.ensureFileEntry(fileName, filePath, cachePath); + 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 fieldFileEntry = fieldEntry.getFileEntry(); + if (fieldFileEntry != null) { + indexLocalFileEntry(fieldFileEntry); + } + return fieldEntry; + } + + private static 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 = ", "; + } + + return builder.toString(); + } + + public boolean isPrimary() { + return primaryEntries.size() != 0; + } + + public ClassEntry getSuperClass() { + return superClass; + } + + 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) { + 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)) { + /* maybe the method's file entry */ + fileEntryToUse = methodEntry.getFileEntry(); + break; + } + } + if (fileEntryToUse == null) { + /* Last chance is the class's file entry. */ + fileEntryToUse = this.fileEntry; + } + } + 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 8cca4480b555..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 @@ -27,13 +27,15 @@ package com.oracle.objectfile.debugentry; 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; 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; /** @@ -83,6 +85,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. */ @@ -99,9 +109,40 @@ 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; + /** + * 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.oopTagsCount = 0; + this.oopCompressShift = 0; + this.oopReferenceSize = 0; + this.oopAlignment = 0; + this.oopAlignShift = 0; } /** @@ -119,36 +160,95 @@ public void installDebugInfo(DebugInfoProvider debugInfoProvider) { */ /* - * Ensure we have a null string in the string section. + * 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. */ + 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 ((oopTagsMask + 1) & oopTagsMask) == 0; + + oopTagsCount = Integer.bitCount(oopTagsMask); + + /* Save amount we need to shift references by when loading from an object field. */ + 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. */ + 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(""); + /* 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 = TypeEntry.canonicalize(debugTypeInfo.typeName()); + DebugTypeKind typeKind = debugTypeInfo.typeKind(); + + 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) -> { /* - * 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(); 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(); @@ -157,93 +257,150 @@ 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 ClassEntry ensureClassEntry(Range range) { - String className = range.getClassName(); - /* - * See if we already have an entry. - */ + 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; + } + + 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. - */ + /* 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) { @@ -252,6 +409,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); } @@ -263,6 +422,10 @@ public ByteOrder getByteOrder() { return byteOrder; } + public LinkedList getTypes() { + return types; + } + public LinkedList getPrimaryClasses() { return primaryClasses; } @@ -281,6 +444,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. * @@ -291,4 +463,47 @@ public StringTable getStringTable() { public int debugStringIndex(String string) { return stringTable.debugStringIndex(string); } + + public boolean useHeapBase() { + return useHeapBase; + } + + 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 oopAlignment() { + return oopAlignment; + } + + 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/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..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 @@ -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(); + @SuppressWarnings("hiding") + DirEntry dirEntry = getDirEntry(); + if (dirEntry == null) { + return ""; + } else { + return dirEntry.getPathString(); + } } public String getFullName() { - return getDirEntry() != null ? getDirEntry().getPath().resolve(getFileName()).toString() : getFileName(); + 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..9b929ca6332d --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/InterfaceClassEntry.java @@ -0,0 +1,85 @@ +/* + * 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(); + } + + @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 nextSize = implementor.getSize(); + + if (nextSize > maxSize) { + maxSize = nextSize; + } + } + return maxSize; + } +} 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..7b84995d6cd5 --- /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) { + 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..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. */ @@ -65,22 +60,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 +87,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..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 @@ -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,20 @@ */ 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 fullMethodName; + private String fullMethodNameWithParams; private int lo; private int hi; private int line; private boolean isDeoptTarget; + private int modifiers; /* * This is null for a primary range. */ @@ -60,41 +55,41 @@ 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.lo = lo; this.hi = hi; this.line = line; this.isDeoptTarget = isDeoptTarget; + this.modifiers = modifiers; this.primary = primary; } @@ -110,24 +105,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 +133,71 @@ public String getFullMethodName() { return fullMethodName; } - public boolean isDeoptTarget() { - return isDeoptTarget; - } - - public String getParamNames() { - return paramNames; + public String getFullMethodNameWithParams() { + return fullMethodNameWithParams; } - 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; + 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..dcc80a0eb71f --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/StructureTypeEntry.java @@ -0,0 +1,116 @@ +/* + * 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 and array 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) { + /* 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(); + 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(fieldModifiers), fieldName, valueTypeName, fieldSize, fieldoffset); + 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); + FieldEntry fieldEntry = new FieldEntry(fileEntry, fieldName, this, valueType, fieldSize, fieldoffset, fieldModifiers); + fields.add(fieldEntry); + return fieldEntry; + } + + 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..afbbf01e117d --- /dev/null +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/TypeEntry.java @@ -0,0 +1,103 @@ +/* + * 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) { + 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 89da1bc263ad..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 @@ -38,34 +38,174 @@ * underlying object file so that the latter can insert appropriate debug info. */ public interface DebugInfoProvider { + boolean useHeapBase(); + /** - * Access details of a specific type. + * Number of bits oops are left shifted by when using compressed oops. */ - interface DebugTypeInfo { - } + int oopCompressShift(); /** - * Access details of a specific compiled method. + * Mask delecting low order bits used for tagging oops. */ - interface DebugCodeInfo { - void debugContext(Consumer action); + int oopTagsMask(); + /** + * 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. + */ + interface DebugFileInfo { /** - * @return the name of the file containing a compiled method excluding any path. + * @return the name of the file containing a file element 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. + * @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 sources of a compiled method - * or {@code null} if sources are not available. + * @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, + HEADER; + + @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"; + case HEADER: + return "header"; + default: + return "???"; + } + } + } + + void debugContext(Consumer action); + + /** + * @return the fully qualified name of the debug type. + */ + String typeName(); + + DebugTypeKind typeKind(); + + int size(); + } + + interface DebugInstanceTypeInfo extends DebugTypeInfo { + int headerSize(); + + Stream fieldInfoProvider(); + + Stream methodInfoProvider(); + + String superName(); + + Stream interfaces(); + } + + interface DebugEnumTypeInfo extends DebugInstanceTypeInfo { + } + + interface DebugInterfaceTypeInfo extends DebugInstanceTypeInfo { + } + + interface DebugArrayTypeInfo extends DebugTypeInfo { + int baseSize(); + + int lengthOffset(); + + String elementType(); + + Stream fieldInfoProvider(); + } + + interface DebugPrimitiveTypeInfo extends DebugTypeInfo { + /* + * NUMERIC excludes LOGICAL types boolean and void + */ + int FLAG_NUMERIC = 1 << 0; + /* + * INTEGRAL excludes FLOATING types float and double + */ + int FLAG_INTEGRAL = 1 << 1; + /* + * SIGNED excludes UNSIGNED type char + */ + int FLAG_SIGNED = 1 << 2; + + int bitCount(); + + char typeChar(); + + int flags(); + } + + 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 extends DebugFileInfo { + void debugContext(Consumer action); /** * @return the fully qualified name of the class owning the compiled method. @@ -108,7 +248,7 @@ interface DebugCodeInfo { /** * @return a string identifying the method parameters. */ - String paramNames(); + String paramSignature(); /** * @return a string identifying the method return type. @@ -130,36 +270,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..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_INFO_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,11 +51,13 @@ public DwarfARangesSectionImpl(DwarfDebugInfo dwarfSections) { @Override public String getSectionName() { - return DW_ARANGES_SECTION_NAME; + return DwarfDebugInfo.DW_ARANGES_SECTION_NAME; } @Override public void createContent() { + assert !contentByteArrayCreated(); + int pos = 0; /* * We need an entry for each compilation unit. @@ -156,6 +155,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 +166,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. @@ -184,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); @@ -204,7 +204,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 +217,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. @@ -237,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); @@ -257,7 +257,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 +270,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 = DwarfDebugInfo.DW_FRAME_SECTION_NAME; @Override public String targetSectionName() { @@ -281,7 +281,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..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 @@ -29,30 +29,6 @@ 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_SECTION_NAME; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_AT_comp_dir; -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_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_stmt_list; -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_data4; -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_strp; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_FRAME_SECTION_NAME; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_TAG_compile_unit; -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.DW_TAG_subprogram; - /** * Section generator for debug_abbrev section. */ @@ -64,18 +40,18 @@ public DwarfAbbrevSectionImpl(DwarfDebugInfo dwarfSections) { @Override public String getSectionName() { - return DW_ABBREV_SECTION_NAME; + return DwarfDebugInfo.DW_ABBREV_SECTION_NAME; } @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 +59,7 @@ public void createContent() { * *
* - * Mon-null entries have the following format. + * Non-null entries have the following format. * *
    * @@ -97,7 +73,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 +87,586 @@ 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 = 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 = 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 = 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. + * + *
    + * + *
      + * + *
    • 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_comp_dir : ... DW_FORM_strp + * *
      • DW_AT_low_pc : ..... DW_FORM_address * *
      • DW_AT_hi_pc : ...... DW_FORM_address * *
      • 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. 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_layout1/class_layout2, tag == DW_TAG_class_type, + * has_children + * + *
        • Dw_AT_name : ........ DW_FORM_strp + * + *
        • Dw_AT_byte_size : ... DW_FORM_data1/2 + * + *
        • Dw_AT_decl_file : ... DW_FORM_data1/2 + * + *
        • Dw_AT_decl_line : ... DW_FORM_data1/2 + * + *
        • Dw_AT_data_location : ... DW_FORM_expr_loc n.b. only for class_layout2 + * + *
        + * + * 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. 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. + * + * 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 + * + *
        • Dw_AT_name : .............. DW_FORM_strp + * + *
        • DW_AT_decl_file : ......... DW_FORM_data1/2 + * + *
        • DW_AT_decl_line : ......... DW_FORM_data1/2 + * + *
        • 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 n.b. 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 n.b. 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 (n.b. nly for + * field_declaration1/2 instance + * + *
        • Dw_AT_artificial : ............. DW_FORM_flag + * + *
        • Dw_AT_accessibility : .......... DW_FORM_data1 + * + *
        • Dw_AT_external : ............... DW_FORM_flag (n.b. only for + * field_declaration3/4 static + * + *
        • Dw_AT_declaration : ............ DW_FORM_flag n.b. 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_data_member_location : ... DW_FORM_data1/2 + * + *
        • 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 n.b. 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 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: 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. + * + *
          + * + *
        • 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 == 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 + * types. So, for example the Java String class looks like + * + *
          + * + *
        • class java.lang.String : public java.lang.Object { + * + *
        • private: + * + *
        • byte[] value; + * + *
        • ... + * + *
        • public: + * + *
        • ... + * + *
        • java.lang.String *concat(java.lang.String *); + * + *
        • ... + * + *
        + * + * 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 + * + *
        • 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_expr_loc + * + *
        + * + * 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 * - *
        • DW_AT_hi_pc : ...... DW_FORM_addr + *
        + * + * 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. + * + *
          + * + *
        • 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 + * + *
        + * + * 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. + * + *
          + * + *
        • 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 + * + *
        + * + * 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 + * 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. + * + *
          + * + *
        • 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. The size of the interface layout is the + * maximum of the sizes for the implementing classes. * - *
      • DW_AT_external : ... DW_FORM_flag + *
          + * + *
        • abbrev_code == interface_layout, tag == union_type, has_children + * + *
        • Dw_AT_name : ....... DW_FORM_strp + * + *
        • DW_AT_location : ... DW_FORM_expr_loc + * + *
        + * + * A second level 1 DIE provides an indirect_layout that wraps the interface layout as its + * 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 + * + *
        • 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 + * + * union java.lang.CharSequence { java.lang.String _java.lang.String; + * java.lang.StringBuffer _java.lang.StringBuffer; ... }; + * */ - 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 +674,61 @@ 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 p) { + int pos = p; + 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 = writeClassLayoutAbbrevs(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); + + /* + * 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; } private int writeAttrType(long code, byte[] buffer, int pos) { @@ -189,66 +747,576 @@ 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(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { + int pos = p; + 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(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, 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(@SuppressWarnings("unused") DebugContext context, int abbrevCode, byte[] buffer, int p) { + int pos = p; + pos = writeAbbrevCode(abbrevCode, 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(DwarfDebugInfo.DW_AT_null, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_null, buffer, pos); + return pos; + } + + private int writeArrayUnitAbbrev(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { + int pos = p; + + 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(DwarfDebugInfo.DW_AT_null, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_null, buffer, pos); + return pos; + } + + private int writePrimitiveTypeAbbrev(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { + int pos = p; + 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(DwarfDebugInfo.DW_AT_null, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_null, buffer, pos); + return pos; + } + + private int writeVoidTypeAbbrev(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { int pos = p; + 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); /* - * Abbrev 1/2 compile unit. + * Now terminate. */ + pos = writeAttrType(DwarfDebugInfo.DW_AT_null, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_null, buffer, pos); + return pos; + } + + private int writeObjectHeaderAbbrev(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { + int pos = p; + 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); + /* + * Now terminate. + */ + pos = writeAttrType(DwarfDebugInfo.DW_AT_null, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_null, buffer, pos); + return pos; + } + + 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; + } + + private int writeClassLayoutAbbrev(@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_compile_unit_1) { - pos = writeAttrType(DW_AT_stmt_list, buffer, pos); - pos = writeAttrForm(DW_FORM_data4, 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); + */ + 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(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; } - @SuppressWarnings("unused") - private int writeMethodAbbrev(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(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); /* - * Abbrev 2 compile unit. + * Now terminate. */ - pos = writeAbbrevCode(DW_ABBREV_CODE_subprogram, buffer, pos); - pos = writeTag(DW_TAG_subprogram, 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_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(DwarfDebugInfo.DW_AT_null, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_null, buffer, pos); + return pos; + } + + private int writeMethodDeclarationAbbrevs(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { + int pos = p; + pos = writeMethodDeclarationAbbrev(context, DwarfDebugInfo.DW_ABBREV_CODE_method_declaration1, buffer, pos); + pos = writeMethodDeclarationAbbrev(context, DwarfDebugInfo.DW_ABBREV_CODE_method_declaration2, buffer, pos); + return pos; + } + + private int writeMethodDeclarationAbbrev(@SuppressWarnings("unused") DebugContext context, int abbrevCode, byte[] buffer, int p) { + int pos = p; + pos = writeAbbrevCode(abbrevCode, buffer, pos); + pos = writeTag(DwarfDebugInfo.DW_TAG_subprogram, buffer, pos); + pos = writeFlag(DwarfDebugInfo.DW_CHILDREN_yes, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_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 */ + // 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(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; + /* 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(@SuppressWarnings("unused") DebugContext context, int abbrevCode, byte[] buffer, int p) { + int pos = p; + pos = writeAbbrevCode(abbrevCode, 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(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(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(DwarfDebugInfo.DW_AT_null, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_null, buffer, pos); + return pos; + } + + private int writeArrayLayoutAbbrev(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { + int pos = p; + + pos = writeAbbrevCode(DwarfDebugInfo.DW_ABBREV_CODE_array_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); + /* + * Now terminate. + */ + pos = writeAttrType(DwarfDebugInfo.DW_AT_null, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_null, buffer, pos); + return pos; + } + + private int writeArrayReferenceAbbrev(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { + int pos = p; + + 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(DwarfDebugInfo.DW_AT_null, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_null, buffer, pos); + return pos; + } + + private int writeInterfaceLayoutAbbrev(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { + int pos = p; + + 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); + /* + * Now terminate. + */ + pos = writeAttrType(DwarfDebugInfo.DW_AT_null, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_null, buffer, pos); + return pos; + } + + private int writeInterfaceReferenceAbbrev(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { + int pos = p; + + 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(DwarfDebugInfo.DW_AT_null, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_null, buffer, pos); + return pos; + } + + private int writeInterfaceImplementorAbbrev(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { + int pos = p; + + 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(DwarfDebugInfo.DW_AT_null, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_null, buffer, pos); + return pos; + } + + private int writeHeaderFieldAbbrev(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { + int pos = p; + + 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(DwarfDebugInfo.DW_AT_null, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_null, buffer, pos); + return pos; + } + + private int writeArrayDataTypeAbbrev(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { + int pos = p; + + 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(DwarfDebugInfo.DW_AT_null, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_null, buffer, pos); + return pos; + } + + private int writeMethodLocationAbbrev(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { + int pos = p; + pos = writeAbbrevCode(DwarfDebugInfo.DW_ABBREV_CODE_method_location, buffer, pos); + pos = writeTag(DwarfDebugInfo.DW_TAG_subprogram, buffer, pos); + pos = writeFlag(DwarfDebugInfo.DW_CHILDREN_yes, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_low_pc, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_addr, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_hi_pc, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_addr, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_external, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_flag, buffer, pos); + pos = writeAttrType(DwarfDebugInfo.DW_AT_specification, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_ref_addr, buffer, pos); + /* + * Now terminate. + */ + pos = writeAttrType(DwarfDebugInfo.DW_AT_null, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_null, buffer, pos); + return pos; + } + + private int writeStaticFieldLocationAbbrev(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { + int pos = p; + + 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? */ + // 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(DwarfDebugInfo.DW_AT_null, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_null, buffer, pos); + return pos; + } + + private int writeSuperReferenceAbbrev(@SuppressWarnings("unused") DebugContext context, byte[] buffer, int p) { + int pos = p; + + 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); + pos = writeAttrType(DwarfDebugInfo.DW_AT_accessibility, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_data1, buffer, pos); + /* + * Now terminate. + */ + pos = writeAttrType(DwarfDebugInfo.DW_AT_null, buffer, pos); + pos = writeAttrForm(DwarfDebugInfo.DW_FORM_null, buffer, pos); + 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); + 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(@SuppressWarnings("unused") DebugContext context, int abbrevCode, byte[] buffer, int p) { + int pos = p; + pos = writeAbbrevCode(abbrevCode, buffer, pos); + pos = writeTag(DwarfDebugInfo.DW_TAG_formal_parameter, buffer, pos); + pos = writeFlag(DwarfDebugInfo.DW_CHILDREN_no, buffer, pos); + /* 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(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); + } + /*- + * 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 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 = DwarfDebugInfo.DW_ARANGES_SECTION_NAME; @Override public String targetSectionName() { @@ -257,7 +1325,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..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 @@ -27,9 +27,13 @@ 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.StructureTypeEntry; +import com.oracle.objectfile.debugentry.TypeEntry; import com.oracle.objectfile.elf.ELFMachine; /** @@ -43,6 +47,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 +56,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_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 = 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_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; + @SuppressWarnings("unused") public static final int DW_AT_decl_column = 0x39; + public static final int DW_AT_decl_file = 0x3a; + @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; @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_data_location = 0x50; + 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 +191,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 +230,41 @@ 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; + @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_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; + 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; + + /* + * A prefix used to label indirect types used to ensure gdb performs oop reference --> raw + * address translation + */ + public static final String INDIRECT_PREFIX = "_z_."; + /* + * 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; private DwarfAbbrevSectionImpl dwarfAbbrevSection; private DwarfInfoSectionImpl dwarfInfoSection; @@ -165,6 +272,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 +298,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 +331,372 @@ 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; + /** + * 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. + */ + private final TypeEntry typeEntry; + + public int getTypeInfoIndex() { + return typeInfoIndex; + } + + 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; + } + + DwarfTypeProperties(TypeEntry typeEntry) { + this.typeEntry = typeEntry; + this.typeInfoIndex = -1; + this.indirectTypeInfoIndex = -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 indirect layout DIE in the debug_info section. + */ + private int indirectLayoutIndex; + /** + * 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(StructureTypeEntry entry) { + super(entry); + this.cuIndex = -1; + this.deoptCUIndex = -1; + this.layoutIndex = -1; + this.indirectLayoutIndex = -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(StructureTypeEntry entry) { + String typeName = entry.getTypeName(); + assert propertiesIndex.get(typeName) == null; + DwarfClassProperties classProperties = new DwarfClassProperties(entry); + 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(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(entry); + } + return classProperties; + } + + private DwarfTypeProperties lookupTypeProperties(String typeName) { + DwarfTypeProperties typeProperties = propertiesIndex.get(typeName); + assert typeProperties != null; + assert typeProperties.getTypeEntry().getTypeName().equals(typeName); + return typeProperties; + } + + @SuppressWarnings("unused") + 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 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; + 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 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); + 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(StructureTypeEntry entry, String fieldName, int pos) { + DwarfClassProperties classProperties; + classProperties = lookupClassProperties(entry); + assert classProperties.getTypeEntry() == entry; + 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(StructureTypeEntry entry, String fieldName) { + DwarfClassProperties classProperties; + classProperties = lookupClassProperties(entry); + assert classProperties.getTypeEntry() == entry; + 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..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,11 +48,13 @@ public DwarfFrameSectionImpl(DwarfDebugInfo dwarfSections) { @Override public String getSectionName() { - return DW_FRAME_SECTION_NAME; + return DwarfDebugInfo.DW_FRAME_SECTION_NAME; } @Override public void createContent() { + assert !contentByteArrayCreated(); + int pos = 0; /* @@ -82,6 +69,8 @@ public void createContent() { @Override public void writeContent(DebugContext context) { + assert contentByteArrayCreated(); + byte[] buffer = getContent(); int size = buffer.length; int pos = 0; @@ -129,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); @@ -151,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); @@ -255,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; @@ -264,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); } @@ -277,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); } } @@ -308,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); @@ -319,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); @@ -331,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); @@ -368,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); } @@ -386,9 +375,9 @@ 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; + private static final String TARGET_SECTION_NAME = DwarfDebugInfo.DW_LINE_SECTION_NAME; @Override public String targetSectionName() { @@ -397,7 +386,7 @@ public String targetSectionName() { private final LayoutDecision.Kind[] targetSectionKinds = { LayoutDecision.Kind.CONTENT, - LayoutDecision.Kind.OFFSET + LayoutDecision.Kind.SIZE }; @Override @@ -407,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 c6e7b37a26fc..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 @@ -26,29 +26,42 @@ 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.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 com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugPrimitiveTypeInfo; /** * Section generator for debug_info section. */ public class DwarfInfoSectionImpl extends DwarfSectionImpl { /** - * an info header section always contains a fixed number of bytes. + * The name of a special DWARF struct type used to model an object header. + */ + public static final String OBJECT_HEADER_STRUCT_NAME = "_objhdr"; + + /** + * An info header section always contains a fixed number of bytes. */ private static final int DW_DIE_HEADER_SIZE = 11; @@ -58,136 +71,1177 @@ public DwarfInfoSectionImpl(DwarfDebugInfo dwarfSections) { @Override public String getSectionName() { - return DW_INFO_SECTION_NAME; + return DwarfDebugInfo.DW_INFO_SECTION_NAME; } @Override - public void createContent() { + public Set getDependencies(Map decisions) { + Set deps = super.getDependencies(decisions); + LayoutDecision ourContent = decisions.get(getElement()).getDecision(LayoutDecision.Kind.CONTENT); /* - * 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. + * Order all content decisions after all size decisions by making info section content + * depend on abbrev section size. */ + String abbrevSectionName = dwarfSections.getAbbrevSectionImpl().getSectionName(); + ELFObjectFile.ELFSection abbrevSection = (ELFObjectFile.ELFSection) getElement().getOwner().elementForName(abbrevSectionName); + LayoutDecision sizeDecision = decisions.get(abbrevSection).getDecision(LayoutDecision.Kind.SIZE); + deps.add(BuildDependency.createOrGet(ourContent, sizeDecision)); + return deps; + } + + @Override + public void createContent() { + assert !contentByteArrayCreated(); 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 & DebugPrimitiveTypeInfo.FLAG_NUMERIC) != 0) { + if (((flags & DebugPrimitiveTypeInfo.FLAG_INTEGRAL) != 0)) { + if ((flags & DebugPrimitiveTypeInfo.FLAG_SIGNED) != 0) { + switch (bitCount) { + case 8: + return DwarfDebugInfo.DW_ATE_signed_char; + default: + assert bitCount == 16 || bitCount == 32 || bitCount == 64; + return DwarfDebugInfo.DW_ATE_signed; + } + } else { + assert bitCount == 16; + return DwarfDebugInfo.DW_ATE_unsigned; + } + } else { + assert bitCount == 32 || bitCount == 64; + return DwarfDebugInfo.DW_ATE_float; + } + } else { + assert bitCount == 1; + return DwarfDebugInfo.DW_ATE_boolean; } - /* write CUs for deopt targets */ + } + + public int generateContent(DebugContext context, byte[] buffer) { + int pos = 0; + + /* 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. + */ + + 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 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 = 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(DwarfDebugInfo.DW_LANG_Java, buffer, pos); + + /* Write child entries for basic Java types. */ + + pos = getTypes().filter(TypeEntry::isPrimitive).reduce(pos, + (pos1, typeEntry) -> { + PrimitiveTypeEntry primitiveTypeEntry = (PrimitiveTypeEntry) typeEntry; + if (primitiveTypeEntry.getBitCount() > 0) { + return writePrimitiveType(context, primitiveTypeEntry, buffer, pos1); + } else { + return writeVoidType(context, primitiveTypeEntry, buffer, pos1); + } + }, + (oldpos, newpos) -> newpos); + + /* Write child entries for object header and array header structs. */ + + pos = getTypes().filter(TypeEntry::isHeader).reduce(pos, + (pos1, typeEntry) -> { + HeaderTypeEntry headerTypeEntry = (HeaderTypeEntry) typeEntry; + return writeHeaderType(context, headerTypeEntry, buffer, pos1); + }, + (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); + /* + * 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); + 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); + /* + * 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); + 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); + /* + * 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); + 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(); + /* 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); + 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 = 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(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); + /* Writing of lo and hi should really be optional. */ + int lo = 0; + log(context, " [0x%08x] lo_pc 0x%08x", pos, lo); + pos = writeAttrAddress(lo, buffer, pos); + int hi = 0; + log(context, " [0x%08x] hi_pc 0x%08x", pos, hi); + pos = writeAttrAddress(hi, buffer, pos); + /* + * Note, there is no need to write a stmt_list (line section idx) for this class unit as the + * class has no code. + */ + + /* Now write the child DIEs starting with the layout and pointer type. */ + + 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); + } + + /* Note, 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 ? 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()); + 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(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. + */ + 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 == 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. */ + + 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; + int layoutIndex = pos; + setLayoutIndex(classEntry, layoutIndex); + log(context, " [0x%08x] class layout", pos); + 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(); + 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(); + 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) { + /* 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(); + 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); + } + /* Now write the child fields. */ + pos = writeSuperReference(context, superTypeOffset, superName, buffer, pos); + pos = writeFields(context, classEntry, buffer, pos); + pos = writeMethodDeclarations(context, classEntry, buffer, pos); + /* + * Write a terminating null attribute. + */ + 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) { + int pos = p; + 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, 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, StructureTypeEntry entry, FieldEntry fieldEntry, byte[] buffer, int p) { + int pos = p; + int modifiers = fieldEntry.getModifiers(); + boolean hasFile = fieldEntry.getFileName().length() > 0; + log(context, " [0x%08x] field definition", pos); + int abbrevCode; + boolean isStatic = Modifier.isStatic(modifiers); + if (!isStatic) { + if (!hasFile) { + abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_field_declaration1; + } else { + abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_field_declaration2; + } + } else { + if (!hasFile) { + abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_field_declaration3; + } else { + abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_field_declaration4; + } + /* Record the position of the declaration to use when we write the definition. */ + setFieldDeclarationIndex(entry, 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) { + 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. */ + } + String valueTypeName = fieldEntry.getValueType().getTypeName(); + /* 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) { + 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.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 ? 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); + 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(); + 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 == 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. + */ + 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, @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 = DwarfDebugInfo.DW_ABBREV_CODE_method_parameter_declaration1; + } else { + abbrevCode = DwarfDebugInfo.DW_ABBREV_CODE_method_parameter_declaration3; + } + log(context, " [0x%08x] <%d> Abbrev Number %d", pos, level, abbrevCode); + pos = writeAbbrevCode(abbrevCode, buffer, pos); + /* We don't have parameter names at present. */ + int typeIdx = getTypeIndex(paramTypeName); + log(context, " [0x%08x] type 0x%x (%s)", pos, typeIdx, paramTypeName); + pos = writeAttrRefAddr(typeIdx, buffer, pos); + if (abbrevCode == DwarfDebugInfo.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 = DwarfDebugInfo.DW_ABBREV_CODE_interface_layout; + log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); + pos = writeAbbrevCode(abbrevCode, buffer, pos); + String name = 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. + */ + 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) { + 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 = 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()); + 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; + + /* Define a pointer type referring to the underlying layout. */ + 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.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; + } + + private int writeInterfaceType(DebugContext context, InterfaceClassEntry interfaceClassEntry, byte[] buffer, int p) { + int pos = p; + + /* Define a pointer type referring to the underlying layout. */ + 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.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; + } + + 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 = 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. */ + 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) (DwarfDebugInfo.DW_OP_breg0 + dwarfSections.getHeapbaseRegister()); + /* + * We have to size the DWARF expression by writing it to the scratch buffer so we can + * write its size as a ULEB before the expression itself. + */ + int size = putByte(regOp, scratch, 0) + putSLEB(offset, scratch, 0); + if (buffer == null) { + /* Add ULEB size to the expression size. */ + return pos + putULEB(size, scratch, 0) + size; + } else { + /* Write the size and expression into the output buffer. */ + pos = putULEB(size, buffer, pos); + pos = putByte(regOp, buffer, pos); + return putSLEB(offset, buffer, pos); + } + } else { + /* Write a relocatable address relative to the heap section start. */ + byte regOp = DwarfDebugInfo.DW_OP_addr; + int size = 9; + /* Write the size and expression into the output buffer. */ + if (buffer == null) { + return pos + putULEB(size, scratch, 0) + size; + } else { + pos = putULEB(size, buffer, pos); + pos = putByte(regOp, buffer, pos); + return putRelocatableHeapOffset(offset, buffer, pos); + } + } + } + + private int writeArrayTypes(DebugContext context, byte[] buffer, int pos) { + log(context, " [0x%08x] array classes", pos); + return getTypes().filter(TypeEntry::isArray).reduce(pos, + (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 = 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, "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. */ + TypeEntry elementType = arrayTypeEntry.getElementType(); + int size = arrayTypeEntry.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. + */ + pos = writeAttrNull(buffer, pos); + + /* Fix up the CU length. */ + patchLength(lengthPos, buffer, pos); + + return pos; + } + + private int writeArrayLayout(DebugContext context, ArrayTypeEntry arrayTypeEntry, TypeEntry elementType, int size, byte[] buffer, int p) { + int pos = p; + 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); + pos = writeAbbrevCode(abbrevCode, buffer, pos); + String name = arrayTypeEntry.getTypeName(); + 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 = 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); + pos = writeFields(context, arrayTypeEntry, buffer, pos); + /* Write a zero length element array field. */ + pos = writeArrayElementField(context, size, arrayDataTypeIdx, buffer, pos); + pos = writeArraySuperReference(context, buffer, pos); + /* + * Write a terminating null attribute. + */ + 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; + + /* + * 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); + 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); + log(context, " [0x%08x] byte_size 0x%x", pos, size); + pos = writeAttrData1((byte) size, buffer, pos); + String elementTypeName = elementType.getTypeName(); + /* 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; + } + + 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 = 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"); + 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, 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); + return writeSuperReference(context, superOffset, superName, buffer, pos); + } + + 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.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; + } + + 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 ? 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, "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(); + 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 == DwarfDebugInfo.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 = DwarfDebugInfo.DW_ABBREV_CODE_method_location; + log(context, " [0x%08x] <1> Abbrev Number %d", pos, abbrevCode); + pos = writeAbbrevCode(abbrevCode, buffer, pos); + log(context, " [0x%08x] lo_pc 0x%08x", pos, range.getLo()); + pos = writeAttrAddress(range.getLo(), buffer, pos); + log(context, " [0x%08x] hi_pc 0x%08x", pos, range.getHi()); + pos = writeAttrAddress(range.getHi(), buffer, pos); + /* + * Should pass true only if method is non-private. + */ + log(context, " [0x%08x] external true", pos); + pos = writeFlag(DwarfDebugInfo.DW_FLAG_true, buffer, pos); + String methodKey = range.getSymbolName(); + 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 +1250,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(DwarfDebugInfo.DW_VERSION_4, scratch, 0); /* Abbrev offset. */ pos += putInt(0, scratch, 0); /* Address size. */ @@ -205,7 +1259,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(DwarfDebugInfo.DW_VERSION_4, buffer, pos); /* Abbrev offset. */ pos = putInt(0, buffer, pos); /* Address size. */ @@ -226,7 +1280,7 @@ private static int findLo(LinkedList classPrimaryEntries, boolean } } } - // we should never get here + /* We should never get here. */ assert false; return 0; } @@ -247,65 +1301,11 @@ private static int findHi(LinkedList classPrimaryEntries, boolean } } } - // should never get here + /* We should never get here. */ assert false; 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 +1326,171 @@ 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 = DwarfDebugInfo.DW_ACCESS_public; + } else if (Modifier.isProtected(modifiers)) { + access = DwarfDebugInfo.DW_ACCESS_protected; + } else if (Modifier.isPrivate(modifiers)) { + access = DwarfDebugInfo.DW_ACCESS_private; + } else { + /* Actually package private -- make it public for now. */ + access = DwarfDebugInfo.DW_ACCESS_public; + } + return writeAttrData1(access, buffer, p); + } + + public int writeIndirectOopConversionExpression(boolean isHub, byte[] buffer, int p) { + int pos = p; + /* + * For an explanation of the conversion rules @see com.oracle.svm.core.heap.ReferenceAccess + * + * 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; + /*- + * We don't need to care about zero oops just mask off the tag bits. + * + * 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] + */ + 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] + * + */ + /* 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 { + /* 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(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. */ + pos = putByte(DwarfDebugInfo.DW_OP_plus, buffer, pos); + assert pos == skipStart + offsetToEnd; + } + /* make sure we added up correctly */ + assert pos == exprStart + exprSize; + } + return pos; + } + /** - * 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 = DwarfDebugInfo.TEXT_SECTION_NAME; @Override public String targetSectionName() { @@ -338,7 +1499,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..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 @@ -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,11 +134,13 @@ public class DwarfLineSectionImpl extends DwarfSectionImpl { @Override public String getSectionName() { - return DW_LINE_SECTION_NAME; + return DwarfDebugInfo.DW_LINE_SECTION_NAME; } @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 +153,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 +278,8 @@ public byte[] getOrDecideContent(Map alre @Override public void writeContent(DebugContext context) { + assert contentByteArrayCreated(); + byte[] buffer = getContent(); int pos = 0; @@ -289,7 +289,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,15 +313,15 @@ 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. */ - 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. */ - 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. @@ -432,8 +432,8 @@ 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(); + assert classEntry.localFilesIdx() == 1; + String primaryClassName = classEntry.getTypeName(); String primaryFileName = classEntry.getFileName(); String file = primaryFileName; int fileIdx = 1; @@ -441,7 +441,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 +458,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,16 +510,17 @@ 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; } String subfile = subFileEntry.getFileName(); int subFileIdx = classEntry.localFilesIdx(subFileEntry); + assert subFileIdx > 0; 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,9 +910,9 @@ 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; + private static final String TARGET_SECTION_NAME = DwarfDebugInfo.DW_STR_SECTION_NAME; @Override public String targetSectionName() { @@ -921,7 +921,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..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,8 @@ 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; import org.graalvm.compiler.debug.DebugContext; @@ -39,8 +41,7 @@ import java.nio.ByteOrder; import java.util.Map; import java.util.Set; - -import static com.oracle.objectfile.elf.dwarf.DwarfDebugInfo.TEXT_SECTION_NAME; +import java.util.stream.Stream; /** * A class from which all DWARF debug sections inherit providing common behaviours. @@ -78,6 +79,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 +113,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; @@ -192,7 +205,17 @@ 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; + } + + 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, DwarfDebugInfo.HEAP_BEGIN_NAME, false, Long.valueOf(l)); pos = putLong(0, buffer, pos); return pos; } @@ -311,6 +334,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 +350,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 +391,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 +425,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 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; + } + 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 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); + } + + protected void setFieldDeclarationIndex(StructureTypeEntry entry, String fieldName, int pos) { + dwarfSections.setFieldDeclarationIndex(entry, fieldName, pos); + } + + protected int getFieldDeclarationIndex(StructureTypeEntry entry, String fieldName) { + if (!contentByteArrayCreated()) { + return 0; + } + return dwarfSections.getFieldDeclarationIndex(entry, 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..d7a70de4132d 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,9 +30,6 @@ import com.oracle.objectfile.debugentry.StringEntry; import org.graalvm.compiler.debug.DebugContext; -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. */ @@ -43,11 +40,13 @@ public DwarfStrSectionImpl(DwarfDebugInfo dwarfSections) { @Override public String getSectionName() { - return DW_STR_SECTION_NAME; + return DwarfDebugInfo.DW_STR_SECTION_NAME; } @Override public void createContent() { + assert !contentByteArrayCreated(); + int pos = 0; for (StringEntry stringEntry : dwarfSections.getStringTable()) { if (stringEntry.isAddToStrSection()) { @@ -62,40 +61,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 = DwarfDebugInfo.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 211865717b70..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 @@ -28,30 +28,58 @@ 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; 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.infrastructure.WrappedJavaMethod; 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.heap.Heap; +import com.oracle.svm.core.heap.ObjectHeader; +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; +import com.oracle.svm.hosted.meta.HostedInstanceClass; +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 @@ -61,17 +89,68 @@ class NativeImageDebugInfoProvider implements DebugInfoProvider { private final DebugContext debugContext; private final NativeImageCodeCache codeCache; @SuppressWarnings("unused") private final NativeImageHeap heap; + boolean useHeapBase; + int compressShift; + int tagsMask; + int referenceSize; + int referenceAlignment; + int primitiveStartOffset; + int referenceStartOffset; NativeImageDebugInfoProvider(DebugContext debugContext, NativeImageCodeCache codeCache, NativeImageHeap heap) { super(); this.debugContext = debugContext; this.codeCache = codeCache; this.heap = heap; + ObjectHeader objectHeader = Heap.getHeap().getObjectHeader(); + ObjectInfo primitiveFields = heap.getObjectInfo(StaticFieldsSupport.getStaticPrimitiveFields()); + ObjectInfo objectFields = heap.getObjectInfo(StaticFieldsSupport.getStaticObjectFields()); + this.tagsMask = objectHeader.getReservedBitsMask(); + if (SubstrateOptions.SpawnIsolates.getValue()) { + CompressEncoding compressEncoding = ImageSingletons.lookup(CompressEncoding.class); + this.useHeapBase = compressEncoding.hasBase(); + this.compressShift = (compressEncoding.hasShift() ? compressEncoding.getShift() : 0); + } else { + this.useHeapBase = false; + this.compressShift = 0; + } + 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(); + } + + @Override + public boolean useHeapBase() { + return useHeapBase; + } + + @Override + public int oopCompressShift() { + return compressShift; + } + + @Override + public int oopReferenceSize() { + return referenceSize; + } + + @Override + public int oopAlignment() { + return referenceAlignment; + } + + @Override + public int oopTagsMask() { + return tagsMask; } @Override public Stream typeInfoProvider() { - return Stream.empty(); + Stream headerTypeInfo = computeHeaderTypeInfo(); + Stream heapTypeInfo = heap.getUniverse().getTypes().stream().map(this::createDebugTypeInfo); + return Stream.concat(headerTypeInfo, heapTypeInfo); } @Override @@ -81,35 +160,101 @@ public Stream codeInfoProvider() { @Override public Stream dataInfoProvider() { - return Stream.empty(); + return heap.getObjects().stream().filter(this::acceptObjectInfo).map(this::createDebugDataInfo); } - /** - * Implementation of the DebugCodeInfo API interface that allows code info to be passed to an - * ObjectFile when generation of debug info is enabled. + static ObjectLayout getObjectLayout() { + return ConfigurationValues.getObjectLayout(); + } + + /* + * 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. */ - private class NativeImageDebugCodeInfo implements DebugCodeInfo { - private final HostedMethod method; - private final ResolvedJavaType javaType; - private final CompilationResult compilation; + protected static ResolvedJavaType getJavaType(HostedType hostedType, boolean wantOriginal) { + ResolvedJavaType javaType; + if (wantOriginal) { + /* Check for wholesale replacement of the original class. */ + javaType = hostedType.getWrapped().getWrappedWithoutResolve(); + 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. */ + HostedType hostedType = hostedMethod.getDeclaringClass(); + ResolvedJavaType javaType = hostedType.getWrapped().getWrappedWithoutResolve(); + ResolvedJavaType originalType = getOriginal(javaType); + if (originalType != null) { + return originalType; + } + /* Check for replacement of the original method only. */ + ResolvedJavaMethod javaMethod = hostedMethod.getWrapped().getWrapped(); + if (javaMethod instanceof SubstitutionMethod) { + return ((SubstitutionMethod) javaMethod).getOriginal().getDeclaringClass(); + } else if (javaMethod instanceof CustomSubstitutionMethod) { + return ((CustomSubstitutionMethod) javaMethod).getOriginal().getDeclaringClass(); + } + return hostedType.getWrapped().getWrapped(); + } + ResolvedJavaMethod javaMethod = hostedMethod.getWrapped().getWrapped(); + return javaMethod.getDeclaringClass(); + } + + 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(); + ResolvedJavaType originalType = getOriginal(javaType); + if (originalType != null) { + return originalType; + } + /* Check for replacement of the original field only. */ + ResolvedJavaField javaField = hostedField.wrapped.wrapped; + if (javaField instanceof SubstitutionField) { + return ((SubstitutionField) javaField).getOriginal().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 { 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(); - this.compilation = compilation; - this.cachePath = SubstrateOptions.getDebugInfoSourceCacheRoot(); + NativeImageDebugFileInfo(HostedType hostedType) { + ResolvedJavaType javaType = getJavaType(hostedType, false); + Class clazz = hostedType.getJavaClass(); SourceManager sourceManager = ImageSingletons.lookup(SourceManager.class); - try (DebugContext.Scope s = debugContext.scope("DebugCodeInfo", declaringClass)) { + try (DebugContext.Scope s = debugContext.scope("DebugFileInfo", hostedType)) { fullFilePath = sourceManager.findAndCacheSource(javaType, clazz, debugContext); } catch (Throwable e) { throw debugContext.handle(e); @@ -117,10 +262,26 @@ private class NativeImageDebugCodeInfo implements DebugCodeInfo { } @SuppressWarnings("try") - @Override - public void debugContext(Consumer action) { - try (DebugContext.Scope s = debugContext.scope("DebugCodeInfo", method)) { - action.accept(debugContext); + NativeImageDebugFileInfo(HostedMethod hostedMethod) { + ResolvedJavaType javaType = getJavaType(hostedMethod, false); + HostedType hostedType = hostedMethod.getDeclaringClass(); + Class clazz = hostedType.getJavaClass(); + 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 = hostedType.getJavaClass(); + 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); } @@ -149,45 +310,601 @@ public Path filePath() { 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())) { + action.accept(debugContext); + } catch (Throwable e) { + throw debugContext.handle(e); + } + } + + public String toJavaName(@SuppressWarnings("hiding") HostedType hostedType) { + return getJavaType(hostedType, true).toJavaName(); + } + + @Override + public String typeName() { + 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 getObjectLayout().getArrayBaseOffset(hostedType.getComponentType().getStorageKind()); + } else if (hostedType instanceof HostedInterface) { + /* Use the size of the header common to all implementors. */ + return getObjectLayout().getFirstFieldOffset(); + } else { + /* 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; + List fieldInfos; + + NativeImageHeaderTypeInfo(String typeName, int size) { + this.typeName = typeName; + this.size = size; + this.fieldInfos = new LinkedList<>(); + } + + void addField(String name, String valueType, int offset, @SuppressWarnings("hiding") 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 = getObjectLayout().getHubOffset(); + int hubFieldSize = referenceSize; + String hubTypeName = "java.lang.Class"; + int idHashOffset = getObjectLayout().getIdentityHashCodeOffset(); + int idHashSize = getObjectLayout().sizeInBytes(JavaKind.Int); + int objHeaderSize = getObjectLayout().getMinimumInstanceObjectSize(); + + /* 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); + + return infos.stream(); + } + + private class NativeImageDebugEnumTypeInfo extends NativeImageDebugInstanceTypeInfo implements DebugEnumTypeInfo { + + NativeImageDebugEnumTypeInfo(HostedInstanceClass enumClass) { + super(enumClass); + } + + @Override + public DebugTypeKind typeKind() { + return DebugTypeKind.ENUM; + } + } + + private class NativeImageDebugInstanceTypeInfo extends NativeImageDebugTypeInfo implements DebugInstanceTypeInfo { + NativeImageDebugInstanceTypeInfo(HostedType hostedType) { + super(hostedType); + } + + @Override + public DebugTypeKind typeKind() { + return DebugTypeKind.INSTANCE; + } + + @Override + public int headerSize() { + return getObjectLayout().getFirstFieldOffset(); + } + + @Override + public Stream fieldInfoProvider() { + 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() { + return Arrays.stream(hostedType.getInterfaces()).map(this::toJavaName); + } + + protected NativeImageDebugFieldInfo createDebugFieldInfo(HostedField field) { + return new NativeImageDebugFieldInfo(field); + } + + 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(); + } + + @Override + public String valueType() { + HostedType valueType = field.getType(); + return toJavaName(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 + */ + if (isStatic() && offset >= 0) { + if (isPrimitive()) { + offset += primitiveStartOffset; + } else { + offset += referenceStartOffset; + } + } + return offset; + } + + @Override + public int size() { + return getObjectLayout().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)) { + 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); + } + } + 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 NativeImageDebugInstanceTypeInfo implements DebugInterfaceTypeInfo { + + NativeImageDebugInterfaceTypeInfo(HostedInterface interfaceClass) { + super(interfaceClass); + } + + @Override + public DebugTypeKind typeKind() { + return DebugTypeKind.INTERFACE; + } + } + + 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 = getObjectLayout().getArrayBaseOffset(arrayKind); + int arrayLengthOffset = getObjectLayout().getArrayLengthOffset(); + int arrayLengthSize = getObjectLayout().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 + public DebugTypeKind typeKind() { + return DebugTypeKind.ARRAY; + } + + @Override + public int baseSize() { + return getObjectLayout().getArrayBaseOffset(arrayClass.getComponentType().getStorageKind()); + } + + @Override + public int lengthOffset() { + return getObjectLayout().getArrayLengthOffset(); + } + + @Override + public String elementType() { + HostedType elementType = arrayClass.getComponentType(); + return toJavaName(elementType); + } + + @Override + public Stream fieldInfoProvider() { + return fieldInfos.stream(); + } + } + + private class NativeImageDebugPrimitiveTypeInfo extends NativeImageDebugTypeInfo implements DebugPrimitiveTypeInfo { + private final HostedPrimitiveType primitiveType; + + NativeImageDebugPrimitiveTypeInfo(HostedPrimitiveType primitiveType) { + super(primitiveType); + this.primitiveType = primitiveType; + } + + @Override + public DebugTypeKind typeKind() { + return DebugTypeKind.PRIMITIVE; + } + + @Override + public int bitCount() { + JavaKind javaKind = primitiveType.getStorageKind(); + return (javaKind == JavaKind.Void ? 0 : javaKind.getBitCount()); + } + + @Override + public char typeChar() { + return primitiveType.getStorageKind().getTypeChar(); + } + + @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(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. + */ + private class NativeImageDebugCodeInfo extends NativeImageDebugFileInfo implements DebugCodeInfo { + private final HostedMethod hostedMethod; + private final CompilationResult compilation; + + NativeImageDebugCodeInfo(HostedMethod method, CompilationResult compilation) { + super(method); + this.hostedMethod = method; + this.compilation = compilation; + } + + @SuppressWarnings("try") + @Override + public void debugContext(Consumer action) { + try (DebugContext.Scope s = debugContext.scope("DebugCodeInfo", hostedMethod)) { + action.accept(debugContext); + } catch (Throwable e) { + throw debugContext.handle(e); + } + } @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 = getJavaType(hostedMethod, true).toJavaName(); + if (name.indexOf('.') >= 0) { + name = name.substring(name.lastIndexOf('.') + 1); + } + if (name.indexOf('$') >= 0) { + name = name.substring(name.lastIndexOf('$') + 1); + } + } + return name; } @Override public String 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); } @@ -211,7 +928,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); @@ -223,7 +940,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); } @@ -235,6 +952,11 @@ public List getFrameSizeChanges() { public boolean isDeoptTarget() { return methodName().endsWith(HostedMethod.METHOD_NAME_DEOPT_SUFFIX); } + + @Override + public int getModifiers() { + return hostedMethod.getModifiers(); + } } /** @@ -291,7 +1013,33 @@ public String className() { @Override public String methodName() { - return method.format("%n"); + ResolvedJavaMethod targetMethod = method; + while (targetMethod instanceof WrappedJavaMethod) { + targetMethod = ((WrappedJavaMethod) targetMethod).getWrapped(); + } + if (targetMethod instanceof SubstitutionMethod) { + targetMethod = ((SubstitutionMethod) targetMethod).getOriginal(); + } else if (targetMethod instanceof CustomSubstitutionMethod) { + targetMethod = ((CustomSubstitutionMethod) targetMethod).getOriginal(); + } + String name = targetMethod.getName(); + if (name.equals("")) { + if (method instanceof HostedMethod) { + name = 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; } @Override @@ -369,4 +1117,74 @@ public Type getType() { return type; } } + + private class NativeImageDebugDataInfo implements DebugDataInfo { + HostedClass hostedClass; + ImageHeapPartition partition; + long offset; + long address; + long size; + 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(); + typeName = hostedClass.toJavaName(); + } + + /* 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 condiiton 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();