diff --git a/docs/reference-manual/native-image/guides/debug-native-executables-with-gdb.md b/docs/reference-manual/native-image/guides/debug-native-executables-with-gdb.md index 1b4312dd0704..2ee54057f0da 100644 --- a/docs/reference-manual/native-image/guides/debug-native-executables-with-gdb.md +++ b/docs/reference-manual/native-image/guides/debug-native-executables-with-gdb.md @@ -7,150 +7,506 @@ permalink: /reference-manual/native-image/guides/debug-native-image-process/ # Debug Native Executables with GDB -A generated native executable is heavily optimized code with minimal symbol information which makes debugging harder. -This can be solved by embedding debug information into the resulting binary at build time. -This information tells the debugger precisely how to interpret the machine code and point it back to the original Java method. - -In this guide you will learn how to debug a native executable using the standard Linux GNU Debugger (GDB). - -> Note: Native Image debugging with GDB currently works on Linux with initial support for macOS. The feature is experimental. - -### Run a Demo - -To build a native executable with debug information, provide the `-g` command-line option for `javac` when compiling the application, and then to the `native-image` builder. -This enables source-level debugging, and the debugger (GDB) then correlates machine instructions with specific source lines in Java files. - -### Prerequisites - -- Linux AMD64 -- GDB 10.1 or higher - -Follow the steps to test debugging a native executable with GDB. The below workflow is known to work on Linux with GDB 10.1. - -1. Download and install the latest GraalVM JDK with Native Image using the [GraalVM JDK Downloader](https://github.com/graalvm/graalvm-jdk-downloader): - ```bash - bash <(curl -sL https://get.graalvm.org/jdk) - ``` - -2. Save the following code to the file named _GDBDemo.java_. - - ```java - public class GDBDemo { - static long fieldUsed = 1000; - - public static void main(String[] args) { - if (args.length > 0) { - int n = -1; - try { - n = Integer.parseInt(args[0]); - } catch (NumberFormatException ex) { - System.out.println(args[0] + " is not a number!"); - } - if (n < 0) { - System.out.println(args[0] + " is negative."); - } - double f = factorial(n); - System.out.println(n + "! = " + f); - } - - if (false) - neverCalledMethod(); - - StringBuilder text = new StringBuilder(); - text.append("Hello World from GraalVM Native Image and GDB in Java.\n"); - System.out.println(text.toString()); - } - - static void neverCalledMethod() { - System.out.println("This method is unreachable and will not be included in the native executable."); - } - - static double factorial(int n) { - if (n == 0) { - return 1; - } - if (n >= fieldUsed) { - return Double.POSITIVE_INFINITY; - } - double f = 1; - while (n > 1) { - f *= n--; - } - return f; - } - } - ``` +### Which GDB to use? -3. Compile it and generate a native executable with debug information: +* Please use GDB 10.2 or later. The debug info is tested via `mx debuginfotest` against 10.2. +* Note that later versions might have slightly different formatting of debugger output (makes e.g. gate checks fail) +* GDB bundled in recent Linux releases works just fine for debugging sessions - ```shell - $JAVA_HOME/bin/javac -g GDBDemo.java - ``` - ```shell - native-image -g -O0 GDBDemo +### Building images with debuginfo + +* Adding `-g` to the `native-image` arguments causes debuginfo to be generated. + * Next to the image, there will be an `.debug` file that contains debuginfo and a `sources` folder that contains Java source files, which the debugger uses to show sources for lineinfo. For example: + ```text + hello_image + hello_image.debug + sources ``` - The `-g` option instructs `native-image` to generate debug information. The resulting native executable will contain debug records in a format GDB understands. + * GDB automatically loads the `.debug` file for a given native executable ``. (There is a link between the image and its `*.debug`-file) +* For a better debugging experience, we recommend combining `-g` with `-O0`. +The latter option disables inlining and other optimizations of the Graal compiler, which otherwise would be observable in the debugger (for example, the debugger may jump back and forth between lines instead of allowing you to step from one line to the next one). +At the same time, `-O0` also enables additional metadata to be collected in the compiler, which then helps the debugger to resolve, for example, local variables. - Notice that you can also pass `-O0` which specifies that no compiler optimizations should be performed. Disabling all optimizations is not required, but in general it makes the debugging experience better. +### Using GDB with the new debuginfo -4. Launch the debugger and run your native executable: +#### Image build information +The `*.debug`-file contains additional information about the image build, which can be accessed as follows: +```text +readelf -p .debug.svm.imagebuild.classpath hello_image.debug +``` +gives a list of all classpath entries that were used to build the image. +```text +String dump of section '.debug.svm.imagebuild.classpath': + [ 0] /home/user/.mx/cache/HAMCREST_e237ae735aac4fa5a7253ec693191f42ef7ddce384c11d29fbf605981c0be077d086757409acad53cb5b9e53d86a07cc428d459ff0f5b00d32a8cbbca390be49/hamcrest.jar + [ b0] /home/user/.mx/cache/JUNIT_5974670c3d178a12da5929ba5dd9b4f5ff461bdc1b92618c2c36d53e88650df7adbf3c1684017bb082b477cb8f40f15dcf7526f06f06183f93118ba9ebeaccce/junit.jar + [ 15a] /home/user/mx/mxbuild/jdk20/dists/jdk9/junit-tool.jar + [ 1a9] /home/user/graal/substratevm/mxbuild/jdk20/com.oracle.svm.test/bin +``` +The following sections are available +* .debug.svm.imagebuild.classpath +* .debug.svm.imagebuild.modulepath +* .debug.svm.imagebuild.arguments +* .debug.svm.imagebuild.java.properties - ```shell - gdb ./gdbdemo - ``` - The `gdb` prompt will open. - -5. Set a breakpoint: type `breakpoint ` to set a breakpoint and `run ` to run the native executable. You can put breakpoints configured by file and line, or by method name. See below the example of a debugging session. - - ```shell - $ gdb ./gdbdemo - GNU gdb (GDB) 10.2 - Copyright (C) 2021 Free Software Foundation, Inc. +#### Where is the main method? +Use +```text +info functions ::main +``` +to find all methods named `main` and then use `b
`, for example: +```text +(gdb) info functions ::main +All functions matching regular expression "::main": + +File hello/Hello.java: +76: void hello.Hello::main(java.lang.String[]*); + +File java/util/Timer.java: +534: void java.util.TimerThread::mainLoop(); +(gdb) b 'hello.Hello::main' + +Breakpoint 1 at 0x83c030: file hello/Hello.java, line 76. +``` + +#### Setting breakpoints +First, find the type of the method you want to set a breakpoint in, for example: +```text +(gdb) info types ArrayList +All types matching regular expression "ArrayList": + +... +File java/util/ArrayList.java: + java.util.ArrayList; + java.util.ArrayList$ArrayListSpliterator; + java.util.ArrayList$Itr; + java.util.ArrayList$ListItr; +... +``` +Now use the following GDB autocompletion +```text +(gdb) b 'java.util.ArrayList:: +``` +Pressing tab twice now shows all `ArrayList` methods to choose from: +```text +java.util.ArrayList::ArrayList(int) java.util.ArrayList::iterator() +java.util.ArrayList::ArrayList(java.util.Collection*) java.util.ArrayList::lastIndexOf(java.lang.Object*) +java.util.ArrayList::add(int, java.lang.Object*) java.util.ArrayList::lastIndexOfRange(java.lang.Object*, int, int) +java.util.ArrayList::add(java.lang.Object*) java.util.ArrayList::listIterator() +java.util.ArrayList::add(java.lang.Object*, java.lang.Object[]*, int) java.util.ArrayList::listIterator(int) +java.util.ArrayList::addAll(int, java.util.Collection*) java.util.ArrayList::nBits(int) +java.util.ArrayList::addAll(java.util.Collection*) java.util.ArrayList::outOfBoundsMsg(int) +... +``` +If we complete with +```text +(gdb) b 'java.util.ArrayList::add` +``` +breakpoints in all variants of `add` are installed. + +#### Arrays +Arrays have a **`data`-field** that can be accessed via an index to get the individual array elements, for example: +```text +Thread 1 "hello_image" hit Breakpoint 1, hello.Hello::main(java.lang.String[]*) (args=0x7ff33f800898) at hello/Hello.java:76 +76 Greeter greeter = Greeter.greeter(args); +(gdb) p args +$1 = (java.lang.String[] *) 0x7ff33f800898 +(gdb) p *args +$2 = { + = { + <_objhdr> = { + hub = 0x1e37be0 + }, }, + members of java.lang.String[]: + len = 4, + data = 0x7ff33f8008a0 +} +(gdb) p args.data +$3 = 0x7ff33f8008a0 +(gdb) ptype args.data +type = class _z_.java.lang.String : public java.lang.String { +} *[0] +``` +Here `args.data` can be accessed via an index. +In this case, the first of the four array elements is a pointer to a String: +```text +(gdb) p args.data[0] +$4 = (_z_.java.lang.String *) 0x27011a +``` + +#### Strings +To see the actual contents of a Java String object, we have to look at its **`value`-field**, for example: +```text +(gdb) p args.data[0] +$4 = (_z_.java.lang.String *) 0x27011a +``` +`args.data[0]` points to a String object. Let's deref: +```text +(gdb) p *args.data[0] +$5 = { + = { + = { + <_objhdr> = { + hub = 0x1bb4780 + }, }, + members of java.lang.String: + value = 0x270118, + hash = 0, + coder = 0 '\000', + hashIsZero = false, + static CASE_INSENSITIVE_ORDER = 0x19d752, ... - Reading symbols from ./gdbdemo... - Reading symbols from /dev/gdbdemo.debug... - (gdb) info func ::main - All functions matching regular expression "::main": - - File GDBDemo.java: - 5: void GDBDemo::main(java.lang.String[]*); - (gdb) b ::factorial - Breakpoint 1 at 0x2d000: file GDBDemo.java, line 32. - (gdb) run 42 - Starting program: /dev/gdbdemo 42 - Thread 1 "gdbdemo" hit Breakpoint 1, GDBDemo::factorial (n=42) at GDBDemo.java:32 - 32 if (n == 0) { - (gdb) info args - n = 42 - (gdb) step - 35 if (n >= fieldUsed) { - (gdb) next - 38 double f = 1; - (gdb) next - 39 while (n > 1) { - (gdb) info locals - f = 1 - (gdb) ... - ``` + static COMPACT_STRINGS = true + }, } +``` +The `value` field holds the String data. +Let's check the type of `value`: +```text +(gdb) p args.data[0].value +$3 = (_z_.byte[] *) 0x250119 +``` +`value` is of type `byte[]`. +As we already learned before, the elements of an array can be accessed via its `data`-field. +```text +(gdb) p args.data[0].value.data +$10 = 0x7ff33f8008c8 "this\376\376\376\376\200G\273\001\030\001'" +``` +GDB is smart enough to interpret the byte-pointer as a C string out of the box. +But in essence, it is an array. +The following gives us the `t` from `this`. +```text +(gdb) p args.data[0].value.data[0] +$13 = 116 't' +``` +The reason for the garbage after the last char is that Java String values are not 0-terminated (unlike C strings). +To know where the garbage starts we can inspect the `len`-field. +```text +(gdb) p args.data[0].value.len +$14 = 4 +``` + +#### Downcasting +Suppose your source uses a variable of static type `Greeter` and you want to inspect its data. +```text +75 public static void main(String[] args) { +76 Greeter greeter = Greeter.greeter(args); +77 greeter.greet(); // Here we might have a NamedGreeter +``` +As you can see, currently GDB only knows about the static type of greeter in line 77: +```text +Thread 1 "hello_image" hit Breakpoint 2, hello.Hello::main(java.lang.String[]*) (args=) at hello/Hello.java:77 +77 greeter.greet(); +(gdb) p greeter +$17 = (hello.Hello$Greeter *) 0x7ff7f9101208 +``` +Also, we are not able to see fields that only exist for the `NamedGreeter` subclass. +```text +(gdb) p *greeter +$18 = { + = { + <_objhdr> = { + hub = 0x1d1cae0 + }, }, } +``` +But we do have the `hub`-field, which points to the class-object of an object. +Therefore, it allows us to determine the runtime-type of the Greeter object at address `0x7ff7f9101208`: +```text +(gdb) p greeter.hub +$19 = (_z_.java.lang.Class *) 0x1d1cae0 +(gdb) p *greeter.hub +$20 = { + = { + = { + <_objhdr> = { + hub = 0x1bec910 + }, }, + members of java.lang.Class: + typeCheckStart = 1188, + name = 0xb94a2, <<<< WE ARE INTERESTED IN THIS FIELD + superHub = 0x90202, + ... + monitorOffset = 8, + optionalIdentityHashOffset = 12, + flags = 0, + instantiationFlags = 3 '\003' + }, } +(gdb) p greeter.hub.name +$21 = (_z_.java.lang.String *) 0xb94a2 +(gdb) p greeter.hub.name.value.data +$22 = 0x7ff7f80705b8 "hello.Hello$NamedGreeter\351\001~*" +``` +So we learned that the actual type of that object is `hello.Hello$NamedGreeter`. We can now cast to that type: +```text +(gdb) set $rt_greeter = ('hello.Hello$NamedGreeter' *) greeter +``` +Now we can inspect the downcasted convenience variable `rt_greeter`: +```text +(gdb) p $rt_greeter +$23 = (hello.Hello$NamedGreeter *) 0x7ff7f9101208 +(gdb) p *$rt_greeter +$24 = { + = { + = { + <_objhdr> = { + hub = 0x1d1cae0 + }, }, }, + members of hello.Hello$NamedGreeter: + name = 0x270119 +} +``` +Now we can see the `name`-field that only exists in the `NamedGreeter` subtype. +```text +(gdb) p $rt_greeter.name +$25 = (_z_.java.lang.String *) 0x270119 +``` +So the `name`-field is of type String. We already know how to see the contents of a String: +```text +(gdb) p $rt_greeter.name.value.data +$26 = 0x7ff7f91008c0 "FooBar\376\376\200G\273\001\027\001'" +``` +##### ⚠️ If the static type that you want to downcast from is a compressed reference then the type used in the downcast also needs to be that of a compressed reference. +For example if you have: +```text +(gdb) p elementData.data[0] + +$38 = (_z_.java.lang.Object *) 0x290fcc +``` +in the internal array of an `ArrayList`, the first entry points to a `java.lang.Object` with a `_z_.` prefix, which denotes that this is a **compressed ref**. +To check what the runtime-type of that object is, we use: +```text +(gdb) p elementData.data[0].hub.name.value.data + +$40 = 0x7ff7f8665600 "java.lang.String=\256\271`" +``` +Now we know that the compressed ref actually points to a `java.lang.String`. +**When we now cast, we must not forget to use the `_z_.` prefix.** +```text +(gdb) p ('_z_.java.lang.String' *) elementData.data[0] + +$41 = (_z_.java.lang.String *) 0x290fcc +(gdb) p *$41 + +$43 = { + = { + = { + <_objhdr> = { + hub = 0x1bb4780 + }, }, + members of java.lang.String: + value = 0x290fce, + ... +``` +To see the contents of that String, we again use: +```text +(gdb) p $41.value.data + +$44 = 0x7ff7f9207e78 "#subsys_name\thierarchy\tnum_cgroups\tenabled" +``` + +#### Using the `this` variable in instance methods +```text +(gdb) bt +#0 hello.Hello$NamedGreeter::greet() (this=0x7ff7f9101208) at hello/Hello.java:71 +#1 0x000000000083c060 in hello.Hello::main(java.lang.String[]*) (args=) at hello/Hello.java:77 +#2 0x0000000000413355 in com.oracle.svm.core.JavaMainWrapper::runCore0() () at com/oracle/svm/core/JavaMainWrapper.java:178 +#3 0x00000000004432e5 in com.oracle.svm.core.JavaMainWrapper::runCore() () at com/oracle/svm/core/JavaMainWrapper.java:136 +#4 com.oracle.svm.core.JavaMainWrapper::doRun(int, org.graalvm.nativeimage.c.type.CCharPointerPointer*) (argc=, argv=) at com/oracle/svm/core/JavaMainWrapper.java:233 +#5 com.oracle.svm.core.JavaMainWrapper::run(int, org.graalvm.nativeimage.c.type.CCharPointerPointer*) (argc=, argv=) at com/oracle/svm/core/JavaMainWrapper.java:219 +#6 com.oracle.svm.core.code.IsolateEnterStub::JavaMainWrapper_run_e6899342f5939c89e6e2f78e2c71f5f4926b786d(int, org.graalvm.nativeimage.c.type.CCharPointerPointer*) (__0=, __1=) +at com/oracle/svm/core/code/IsolateEnterStub.java:1 +(gdb) p this +$1 = (hello.Hello$NamedGreeter *) 0x7ff7f9001218 +(gdb) p *this +$2 = { + = { + = { + <_objhdr> = { + hub = 0x1de2260 + }, }, }, + members of hello.Hello$NamedGreeter: + name = 0x25011b +} +(gdb) p this.name +$3 = (_z_.java.lang.String *) 0x270119 +``` +Just like in Java or C++ code, in instance-methods, prefixing with `this.` is not needed. +```text +(gdb) p name +$7 = (_z_.java.lang.String *) 0x270119 +(gdb) p name.value.data +$8 = 0x7ff7f91008c0 "FooBar\376\376\200G\273\001\027\001'" +``` + +#### Accessing static fields +While static fields are shown whenever we print an instance of an object sometimes, we just want to see the value of a specific static field. +```text +(gdb) p 'java.math.BigDecimal::BIG_TEN_POWERS_TABLE' +$23 = (_z_.java.math.BigInteger[] *) 0x132b95 +``` +To get a list of all static fields, use: +```text +(gdb) info variables :: +``` + +#### Inspecting `.class` Objects +For every Java type in the image, there exists an easy way to access its class object (aka the hub). +```text +(gdb) info types PrintStream +All types matching regular expression "PrintStream": + +... +File java/io/PrintStream.java: + java.io.PrintStream; + java.io.PrintStream$1; +... +``` +To access the hub of `java.io.PrintStream`, we can use the `.class` suffix: +```text +(gdb) p 'java.io.PrintStream.class' +$4 = { + = { + <_objhdr> = { + hub = 0x1bec910 + }, }, + members of java.lang.Class: + typeCheckStart = 1340, + name = 0xbab58, + superHub = 0x901ba, + ... + sourceFileName = 0xbab55, + classInitializationInfo = 0x14d189, + module = 0x14cd8d, + nestHost = 0xde78d, + simpleBinaryName = 0x0, + companion = 0x149856, + signature = 0x0, + ... +} +``` +This allows us, for example, to check which module `java.io.PrintStream` belongs to: +```text +(gdb) p 'java.io.PrintStream.class'.module.name.value.data +$12 = 0x7ff7f866b000 "java.base" +``` - -In case your native executable segfaults, you can print the backtrace of the entire stack (`bt`). +#### Inlined methods +Setting a breakpoint in `PrintStream.writeln` +```text +(gdb) b java.io.PrintStream::writeln +Breakpoint 2 at 0x4080cb: java.io.PrintStream::writeln. (35 locations) +``` +Now we navigate to +```text +(gdb) bt +#0 java.io.BufferedWriter::min(int, int) (this=, a=8192, b=14) at java/io/BufferedWriter.java:216 +#1 java.io.BufferedWriter::implWrite(java.lang.String*, int, int) (this=0x7ff7f884e828, s=0x7ff7f9101230, off=, len=) at java/io/BufferedWriter.java:329 +#2 0x000000000084c50d in java.io.BufferedWriter::write(java.lang.String*, int, int) (this=, s=, off=, len=) at java/io/BufferedWriter.java:313 +#3 0x0000000000901369 in java.io.Writer::write(java.lang.String*) (this=, str=) at java/io/Writer.java:278 +#4 0x00000000008df465 in java.io.PrintStream::implWriteln(java.lang.String*) (this=0x7ff7f87e67b8, s=) at java/io/PrintStream.java:846 +#5 0x00000000008e10a5 in java.io.PrintStream::writeln(java.lang.String*) (this=0x7ff7f87e67b8, s=) at java/io/PrintStream.java:826 +#6 0x000000000083c00c in java.io.PrintStream::println(java.lang.String*) (this=, x=) at java/io/PrintStream.java:1168 +#7 hello.Hello$NamedGreeter::greet() (this=) at hello/Hello.java:71 +#8 0x000000000083c060 in hello.Hello::main(java.lang.String[]*) (args=) at hello/Hello.java:77 +#9 0x0000000000413355 in com.oracle.svm.core.JavaMainWrapper::runCore0() () at com/oracle/svm/core/JavaMainWrapper.java:178 +#10 0x00000000004432e5 in com.oracle.svm.core.JavaMainWrapper::runCore() () at com/oracle/svm/core/JavaMainWrapper.java:136 +#11 com.oracle.svm.core.JavaMainWrapper::doRun(int, org.graalvm.nativeimage.c.type.CCharPointerPointer*) (argc=, argv=) at com/oracle/svm/core/JavaMainWrapper.java:233 +#12 com.oracle.svm.core.JavaMainWrapper::run(int, org.graalvm.nativeimage.c.type.CCharPointerPointer*) (argc=, argv=) at com/oracle/svm/core/JavaMainWrapper.java:219 +#13 com.oracle.svm.core.code.IsolateEnterStub::JavaMainWrapper_run_e6899342f5939c89e6e2f78e2c71f5f4926b786d(int, org.graalvm.nativeimage.c.type.CCharPointerPointer*) (__0=, __1=) + at com/oracle/svm/core/code/IsolateEnterStub.java:1 +``` +If we query extra info about the top frame, we see +```text +(gdb) info frame +Stack level 0, frame at 0x7fffffffdb20: + rip = 0x84af8a in java.io.BufferedWriter::min(int, int) (java/io/BufferedWriter.java:216); saved rip = 0x84c50d + inlined into frame 1 + source language unknown. + Arglist at unknown address. + Locals at unknown address, Previous frame's sp in rsp +``` +that `min` got inlined into `implWrite`. +Now stepping into the use-site of `min`, we see +```text +(gdb) bt +#0 java.lang.String::getChars(int, int, char[]*, int) (this=0x7ff7f9101230, srcBegin=0, srcEnd=14, dst=0x7ff7f858ac58, dstBegin=0) at java/lang/String.java:1688 +#1 java.io.BufferedWriter::implWrite(java.lang.String*, int, int) (this=0x7ff7f884e828, s=0x7ff7f9101230, off=, len=) at java/io/BufferedWriter.java:330 +... +``` +that value `14` was returned by `min` (as expected). -The debugger points machine instructions back from the binary to specific source lines in Java files. Note that single stepping within a compiled method includes file and line number information for inlined code. GDB may switch files even though you are still in the same compiled method. +#### Calling `svm_dbg_`-helper functions during debugging -Most of the regular debugging actions are supported by GDB, namely: +When the image gets built with `-H:+IncludeDebugHelperMethods`, additional `@CEntryPoint`-functions are defined that can be called from GDB during debugging, for example: +```text +(gdb) p greeter +$3 = (hello.Hello$Greeter *) 0x7ffff6881900 +``` +Here again, we have a local named `greeter` with the static-type `hello.Hello$Greeter`. +To see its runtime-type, we can use the methods already described above. +Alternatively, we can make use of the `svm_dbg_`-helper functions. +For example, we can call +```text +void svm_dbg_print_hub(graal_isolatethread_t* thread, size_t hubPtr) +``` +from within the running debug session. We have to pass a value for `graal_isolatethread_t` and the absolute address of the hub we want to get printed. +In most situations, the value for `graal_isolatethread_t` is just the value of the current `IsolateThread` that can be found in a platform-specific register: - - single stepping 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 and printing objects at different levels of generality - - access through object networks via path expressions - - reference by name to methods and static field data +| Platform | Register | +| --------- | -------- | +| `amd64` | `$r15` | +| `aarch64` | `$r28` | -The generation of debug information is implemented by modeling the Java program as an equivalent C++ program. Since GDB was primarily designed for debugging C (and C++), there are certain considerations to be taken into account when debugging Java applications. -Read more about Native Image debugging support in the [reference documentation](../DebugInfo.md#special-considerations-for-debugging-java-from-gdb). +Finally, before we can call `svm_dbg_print_hub` we also have to make sure we have the **absolute address** of the hub we want to print. Using +```text +(gdb) p greeter.hub +$4 = (_z_.java.lang.Class *) 0x837820 +``` +reveals that in the current situation, the `hub`-field in `greeter` holds a compressed reference to the hub (the `hub-type` is prefixed with `_z_.`). +Thus, we first need to get the absolute address of the hub field by using another `svm_dbg_`-helper method. +```text +(gdb) call svm_dbg_obj_uncompress($r15, greeter.hub) +$5 = 140737339160608 +(gdb) p/x $5 +$6 = 0x7ffff71b7820 +``` +With the help of calling `svm_dbg_obj_uncompress`, we now know that the hub is located at address `0x7ffff71b7820` and we can finally call `svm_dbg_print_hub`: +```text +(gdb) call (void) svm_dbg_print_hub($r15, 0x7ffff71b7820) +hello.Hello$NamedGreeter +``` +Both calls to `svm_dbg_`-helper can be combined into a single command line: +```text +(gdb) call (void) svm_dbg_print_hub($r15, svm_dbg_obj_uncompress($r15, greeter.hub)) +hello.Hello$NamedGreeter +``` -### Related Documentation +##### The following `svm_dbg_`-helper methods are currently defined: -- [Debug Info Feature](../DebugInfo.md) \ No newline at end of file +```text +int svm_dbg_ptr_isInImageHeap(graal_isolatethread_t* thread, size_t ptr); +int svm_dbg_ptr_isObject(graal_isolatethread_t* thread, size_t ptr); +int svm_dbg_hub_getLayoutEncoding(graal_isolatethread_t* thread, size_t hubPtr); +int svm_dbg_hub_getArrayElementSize(graal_isolatethread_t* thread, size_t hubPtr); +int svm_dbg_hub_getArrayBaseOffset(graal_isolatethread_t* thread, size_t hubPtr); +int svm_dbg_hub_isArray(graal_isolatethread_t* thread, size_t hubPtr); +int svm_dbg_hub_isPrimitiveArray(graal_isolatethread_t* thread, size_t hubPtr); +int svm_dbg_hub_isObjectArray(graal_isolatethread_t* thread, size_t hubPtr); +int svm_dbg_hub_isInstance(graal_isolatethread_t* thread, size_t hubPtr); +int svm_dbg_hub_isReference(graal_isolatethread_t* thread, size_t hubPtr); +long long int svm_dbg_obj_getHub(graal_isolatethread_t* thread, size_t objPtr); +long long int svm_dbg_obj_getObjectSize(graal_isolatethread_t* thread, size_t objPtr); +int svm_dbg_obj_getArrayElementSize(graal_isolatethread_t* thread, size_t objPtr); +long long int svm_dbg_obj_getArrayBaseOffset(graal_isolatethread_t* thread, size_t objPtr); +int svm_dbg_obj_isArray(graal_isolatethread_t* thread, size_t objPtr); +int svm_dbg_obj_isPrimitiveArray(graal_isolatethread_t* thread, size_t objPtr); +int svm_dbg_obj_isObjectArray(graal_isolatethread_t* thread, size_t objPtr); +int svm_dbg_obj_isInstance(graal_isolatethread_t* thread, size_t objPtr); +int svm_dbg_obj_isReference(graal_isolatethread_t* thread, size_t objPtr); +long long int svm_dbg_obj_uncompress(graal_isolatethread_t* thread, size_t compressedPtr); +long long int svm_dbg_obj_compress(graal_isolatethread_t* thread, size_t objPtr); +int svm_dbg_string_length(graal_isolatethread_t* thread, size_t strPtr); +void svm_dbg_print_hub(graal_isolatethread_t* thread, size_t hubPtr); +void svm_dbg_print_obj(graal_isolatethread_t* thread, size_t objPtr); +void svm_dbg_print_string(graal_isolatethread_t* thread, size_t strPtr); +void svm_dbg_print_fatalErrorDiagnostics(graal_isolatethread_t* thread, size_t sp, void * ip); +void svm_dbg_print_locationInfo(graal_isolatethread_t* thread, size_t mem); +``` \ No newline at end of file diff --git a/substratevm/mx.substratevm/mx_substratevm.py b/substratevm/mx.substratevm/mx_substratevm.py index ede9d389dd6f..3ae6765827f3 100644 --- a/substratevm/mx.substratevm/mx_substratevm.py +++ b/substratevm/mx.substratevm/mx_substratevm.py @@ -791,7 +791,6 @@ def _collector(x): raise Exception('Unexpected output: ' + str(actual_output) + " != " + str(expected_output)) def _debuginfotest(native_image, path, build_only, with_isolates_only, args): - mkpath(path) mx.log("path=%s"%path) sourcepath = mx.project('com.oracle.svm.test').source_dirs()[0] mx.log("sourcepath=%s"%sourcepath) @@ -809,31 +808,33 @@ def _debuginfotest(native_image, path, build_only, with_isolates_only, args): args.append("-D" + key + "=" + value) - native_image_args = ["--native-image-info", "-H:Path=" + path, + native_image_args = ["--native-image-info", '-H:+VerifyNamingConventions', '-cp', classpath('com.oracle.svm.test'), '-Dgraal.LogFile=graal.log', '-g', '-H:+SourceLevelDebug', '-H:DebugInfoSourceSearchPath=' + sourcepath, - '-H:DebugInfoSourceCacheRoot=' + join(path, 'sources'), # We do not want to step into class initializer, so initialize everything at build time. '--initialize-at-build-time=hello', 'hello.Hello'] + args - def build_debug_test(extra_args): - build_args = native_image_args + extra_args + def build_debug_test(variant_name, extra_args): + per_build_path = join(path, variant_name) + mkpath(per_build_path) + build_args = native_image_args + extra_args + [ + '-o', join(per_build_path, 'hello_image') + ] mx.log('native_image {}'.format(build_args)) - native_image(build_args) + return native_image(build_args) # build with and without Isolates and check both work if '--libc=musl' in args: os.environ.update({'debuginfotest_musl' : 'yes'}) testhello_py = join(suite.dir, 'mx.substratevm', 'testhello.py') - hello_binary = join(path, 'hello.hello') - build_debug_test(['-H:+SpawnIsolates']) + hello_binary = build_debug_test('isolates_on', ['-H:+SpawnIsolates']) if mx.get_os() == 'linux' and not build_only: os.environ.update({'debuginfotest_arch' : mx.get_arch()}) if mx.get_os() == 'linux' and not build_only: @@ -841,7 +842,7 @@ def build_debug_test(extra_args): mx.run([os.environ.get('GDB_BIN', 'gdb'), '-ex', 'python "ISOLATES=True"', '-x', testhello_py, hello_binary]) if not with_isolates_only: - build_debug_test(['-H:-SpawnIsolates']) + hello_binary = build_debug_test('isolates_off', ['-H:-SpawnIsolates']) if mx.get_os() == 'linux' and not build_only: os.environ.update({'debuginfotest_isolates' : 'no'}) mx.run([os.environ.get('GDB_BIN', 'gdb'), '-ex', 'python "ISOLATES=False"', '-x', testhello_py, hello_binary]) @@ -1247,7 +1248,7 @@ def debuginfotest(args, config=None): parser = ArgumentParser(prog='mx debuginfotest') all_args = ['--output-path', '--build-only', '--with-isolates-only'] masked_args = [_mask(arg, all_args) for arg in args] - parser.add_argument(all_args[0], metavar='', nargs=1, help='Path of the generated image', default=[svmbuild_dir(suite)]) + parser.add_argument(all_args[0], metavar='', nargs=1, help='Path of the generated image', default=[svmbuild_dir()]) parser.add_argument(all_args[1], action='store_true', help='Only build the native image') parser.add_argument(all_args[2], action='store_true', help='Only build and test the native image with isolates') parser.add_argument('image_args', nargs='*', default=[]) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/DebugHelper.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/DebugHelper.java index 6520d15983ed..cfdb4d115a98 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/DebugHelper.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/DebugHelper.java @@ -260,11 +260,12 @@ public static void printHub(@SuppressWarnings("unused") IsolateThread thread, Po Log.log().string(hub.getName()).newline(); } - @Uninterruptible(reason = "Called with a raw object pointer.") + @Uninterruptible(reason = "Called with a raw object pointer.", calleeMustBe = false) @CEntryPoint(name = "svm_dbg_print_obj", include = IncludeDebugHelperMethods.class, publishAs = Publish.SymbolOnly) @CEntryPointOptions(prologue = SetThreadAndHeapBasePrologue.class, epilogue = NoEpilogue.class) public static void printObject(@SuppressWarnings("unused") IsolateThread thread, Pointer objPtr) { SubstrateDiagnostics.printObjectInfo(Log.log(), objPtr); + Log.log().newline(); } @Uninterruptible(reason = "Called with a raw object pointer.", calleeMustBe = false)