From 51d1e6cb461d458d22f24a66e54ae02834364a12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Wed, 10 May 2023 13:11:20 +0200 Subject: [PATCH 01/10] Add Debuginfo Generation Walkthrough guide --- .../guides/debuginfo-walkthrough.md | 433 ++++++++++++++++++ .../native-image/guides/guides.md | 1 + 2 files changed, 434 insertions(+) create mode 100644 docs/reference-manual/native-image/guides/debuginfo-walkthrough.md diff --git a/docs/reference-manual/native-image/guides/debuginfo-walkthrough.md b/docs/reference-manual/native-image/guides/debuginfo-walkthrough.md new file mode 100644 index 000000000000..f9b3c8c68c43 --- /dev/null +++ b/docs/reference-manual/native-image/guides/debuginfo-walkthrough.md @@ -0,0 +1,433 @@ +--- +layout: ni-docs +toc_group: how-to-guides +link_title: Debuginfo Generation Walkthrough +permalink: /reference-manual/native-image/guides/debuginfo-walkthrough/ +--- + +# Debuginfo Generation Walkthrough + +### Which GDB to use? + +* Please use GDB 10.2 or higher. Gate checks (`mx debuginfotest`) use 10.2. +* 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 + +### Building images with debuginfo + +* Adding `-g` to the native-image arguments causes debuginfo to be generated. + * Next to the image there will be a `.debug` file that contains debuginfo and a `sources` folder that contains java source files needed b the debugger to show sources for lineinfo debuginfo. For example: + ```text + hello_image + hello_image.debug + sources + ``` + * GDB automatically loads the `.debug` file for a given native executable ``. (There is a link between the image and its `*.debug`-file) +* Combining `-g` with `-O0` generally gives a better debugging experience. Because `-O0` among other things + * restricts inlining (helps, because accessing params and locals in inlined methods does not work reliably) + * sets `GraphBuilderConfiguration#withRetainLocalVariables` + * enables `SubstrateOptions.SourceLevelDebug` which sets `GraphBuilderConfiguration.withFullInfopoints` + +### Using GDB with the new debuginfo + +#### Image build information +When `-g` is used, in addition to debuginfo, image build information gets recorded in the `*.debug`-file. For example: +```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 +```text +.debug.svm.imagebuild.classpath +.debug.svm.imagebuild.modulepath +.debug.svm.imagebuild.arguments +.debug.svm.imagebuild.java.properties +``` + +#### Where is main? +Use +```text +info functions ::main +``` +to find all methods named `main` and then use `b
`. E.g. +```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. E.g. +```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** with that can be accessed via index to access the individual array elements, e.g. +```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 index. E.g. 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**, e.g. +```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, + ... + static COMPACT_STRINGS = true + }, } +``` +The `value` field holds the String data. Let's see what type `value` is: +```text +(gdb) p args.data[0].value +$3 = (_z_.byte[] *) 0x250119 +``` +It's a byte array. 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's 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 that allows us to see that the runtime-type of the Greeter object at address `0x7ff7f9101208` is: +```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, you can see that the first entry points to a `java.lang.Object` via a **compressed ref**. This can be seen by the `_z_.` prefix in the type-name. +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 use again +```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 (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" +``` + +#### 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). \ No newline at end of file diff --git a/docs/reference-manual/native-image/guides/guides.md b/docs/reference-manual/native-image/guides/guides.md index 7a126b52dc11..0bd37b165479 100644 --- a/docs/reference-manual/native-image/guides/guides.md +++ b/docs/reference-manual/native-image/guides/guides.md @@ -23,6 +23,7 @@ Here you will learn how to: - [Containerise a Native Executable and Run in a Docker Container](containerise-native-executable-with-docker.md) - [Create a Heap Dump from a Native Executable](create-heap-dump-from-native-executable.md) - [Debug Native Executables with GDB](debug-native-executables-with-gdb.md) +- [Debuginfo Generation Walkthrough](debuginfo-walkthrough.md) - [Include Resources in a Native Executable](include-resources.md) - [Optimize a Native Executable with Profile-Guided Optimizations](optimize-native-executable-with-pgo.md) - [Use GraalVM Dashboard to Optimize the Size of a Native Executable](use-graalvm-dashboard.md) From cd44411d3260a8ab6c909c42358deae576e8c168 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Wed, 10 May 2023 13:12:18 +0200 Subject: [PATCH 02/10] Ensure images generated by `mx debuginfotest` are reusable --- substratevm/mx.substratevm/mx_substratevm.py | 21 ++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) 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=[]) From 4d4cd40f149686b9ed3373580d642369114549ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Thu, 11 May 2023 11:37:19 +0200 Subject: [PATCH 03/10] Add missing newline for the output of svm_dbg_print_obj --- .../src/com/oracle/svm/core/DebugHelper.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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) From 1143e800083202f75681cdc6af61e8e422cd7f32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Thu, 11 May 2023 14:07:29 +0200 Subject: [PATCH 04/10] Add info about how to call `svm_dbg_`-helper functions during debugging --- .../guides/debuginfo-walkthrough.md | 78 ++++++++++++++++++- 1 file changed, 77 insertions(+), 1 deletion(-) diff --git a/docs/reference-manual/native-image/guides/debuginfo-walkthrough.md b/docs/reference-manual/native-image/guides/debuginfo-walkthrough.md index f9b3c8c68c43..134c90f718d6 100644 --- a/docs/reference-manual/native-image/guides/debuginfo-walkthrough.md +++ b/docs/reference-manual/native-image/guides/debuginfo-walkthrough.md @@ -430,4 +430,80 @@ Now stepping into the use-site of `min` we see #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). \ No newline at end of file +that value `14` was returned by `min` (as expected). + +#### Calling `svm_dbg_`-helper functions during debugging + +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: + +| Platform | Register | +| --------- | -------- | +| `amd64` | `$r15` | +| `aarch64` | `$r28` | + +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. This can be seen from the `_z_.`-prefix of the `hub`-type. +Thus, we need first need 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 +``` + +##### The following `svm_dbg_`-helper methods are currently defined: + +```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 From bf028a9873b64b9430c9c324b7ef63ca3f57aeb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Tue, 16 May 2023 08:06:57 +0000 Subject: [PATCH 05/10] Reword based on review feedback --- .../guides/debuginfo-walkthrough.md | 73 ++++++++++--------- 1 file changed, 39 insertions(+), 34 deletions(-) diff --git a/docs/reference-manual/native-image/guides/debuginfo-walkthrough.md b/docs/reference-manual/native-image/guides/debuginfo-walkthrough.md index 134c90f718d6..c4cae87aac8f 100644 --- a/docs/reference-manual/native-image/guides/debuginfo-walkthrough.md +++ b/docs/reference-manual/native-image/guides/debuginfo-walkthrough.md @@ -9,14 +9,14 @@ permalink: /reference-manual/native-image/guides/debuginfo-walkthrough/ ### Which GDB to use? -* Please use GDB 10.2 or higher. Gate checks (`mx debuginfotest`) use 10.2. -* Later versions might have slightly different formatting of debugger output (makes e.g. gate checks fail) +* 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 ### Building images with debuginfo -* Adding `-g` to the native-image arguments causes debuginfo to be generated. - * Next to the image there will be a `.debug` file that contains debuginfo and a `sources` folder that contains java source files needed b the debugger to show sources for lineinfo debuginfo. For example: +* 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 @@ -31,7 +31,7 @@ permalink: /reference-manual/native-image/guides/debuginfo-walkthrough/ ### Using GDB with the new debuginfo #### Image build information -When `-g` is used, in addition to debuginfo, image build information gets recorded in the `*.debug`-file. For example: +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 ``` @@ -51,12 +51,12 @@ The following sections are available .debug.svm.imagebuild.java.properties ``` -#### Where is main? +#### Where is the main function? Use ```text info functions ::main ``` -to find all methods named `main` and then use `b
`. E.g. +to find all methods named `main` and then use `b
`, for example: ```text (gdb) info functions ::main All functions matching regular expression "::main": @@ -72,7 +72,7 @@ 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. E.g. +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": @@ -89,7 +89,7 @@ Now use the following GDB autocompletion ```text (gdb) b 'java.util.ArrayList:: ``` -Pressing tab twice now shows all ArrayList methods to choose from: +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*) @@ -107,7 +107,7 @@ If we complete with breakpoints in all variants of `add` are installed. #### Arrays -Arrays have a **`data`-field** with that can be accessed via index to access the individual array elements, e.g. +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); @@ -129,14 +129,15 @@ $3 = 0x7ff33f8008a0 type = class _z_.java.lang.String : public java.lang.String { } *[0] ``` -Here `args.data` can be accessed via index. E.g. The first of the four array elements is a pointer to a String +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**, e.g. +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 @@ -160,18 +161,21 @@ $5 = { static COMPACT_STRINGS = true }, } ``` -The `value` field holds the String data. Let's see what type `value` is: +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 ``` -It's a byte array. As we already learned before, the elements of an array can be accessed via its `data`-field. +`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's an array. The following gives us the `t` from `this`. +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' @@ -268,15 +272,15 @@ For example if you have: $38 = (_z_.java.lang.Object *) 0x290fcc ``` -in the internal array of an ArrayList, you can see that the first entry points to a `java.lang.Object` via a **compressed ref**. This can be seen by the `_z_.` prefix in the type-name. -To check what the runtime-type of that object is we use: +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.** +**When we now cast, we must not forget to use the `_z_.` prefix.** ```text (gdb) p ('_z_.java.lang.String' *) elementData.data[0] @@ -293,7 +297,7 @@ $43 = { value = 0x290fce, ... ``` -To see the contents of that String we use again +To see the contents of that String, we again use: ```text (gdb) p $41.value.data @@ -326,7 +330,7 @@ $2 = { (gdb) p this.name $3 = (_z_.java.lang.String *) 0x270119 ``` -Just like in Java (C++) code, in instance-methods, prefixing with `this.` is not needed. +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 @@ -335,19 +339,18 @@ $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. +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 +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). +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": @@ -358,7 +361,7 @@ File java/io/PrintStream.java: java.io.PrintStream$1; ... ``` -To access the hub of `java.io.PrintStream` we can use the `.class` suffix. +To access the hub of `java.io.PrintStream`, we can use the `.class` suffix: ```text (gdb) p 'java.io.PrintStream.class' $4 = { @@ -412,7 +415,7 @@ Now we navigate to #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 +If we query extra info about the top frame, we see ```text (gdb) info frame Stack level 0, frame at 0x7fffffffdb20: @@ -423,7 +426,7 @@ Stack level 0, frame at 0x7fffffffdb20: 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 +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 @@ -434,18 +437,20 @@ that value `14` was returned by `min` (as expected). #### Calling `svm_dbg_`-helper functions during debugging -When the image gets built with `-H:+IncludeDebugHelperMethods` additional `@CEntryPoint`-functions are defined that can be called from GDB during debugging. For example: +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 +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: +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: | Platform | Register | | --------- | -------- | @@ -457,15 +462,15 @@ Finally, before we can call `svm_dbg_print_hub` we also have to make sure we hav (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. This can be seen from the `_z_.`-prefix of the `hub`-type. -Thus, we need first need get the absolute address of the hub field by using another `svm_dbg_`-helper method. +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`: +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 From 47fd66a225b86af7cd030efcd89e9a2fe479e64f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Tue, 16 May 2023 11:03:51 +0200 Subject: [PATCH 06/10] Turn text section into listing --- .../native-image/guides/debuginfo-walkthrough.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/docs/reference-manual/native-image/guides/debuginfo-walkthrough.md b/docs/reference-manual/native-image/guides/debuginfo-walkthrough.md index c4cae87aac8f..a0b59f562493 100644 --- a/docs/reference-manual/native-image/guides/debuginfo-walkthrough.md +++ b/docs/reference-manual/native-image/guides/debuginfo-walkthrough.md @@ -44,12 +44,10 @@ String dump of section '.debug.svm.imagebuild.classpath': [ 1a9] /home/user/graal/substratevm/mxbuild/jdk20/com.oracle.svm.test/bin ``` The following sections are available -```text -.debug.svm.imagebuild.classpath -.debug.svm.imagebuild.modulepath -.debug.svm.imagebuild.arguments -.debug.svm.imagebuild.java.properties -``` +* .debug.svm.imagebuild.classpath +* .debug.svm.imagebuild.modulepath +* .debug.svm.imagebuild.arguments +* .debug.svm.imagebuild.java.properties #### Where is the main function? Use From 48c080d5f97e9aedcf7e482bd970963cdcc76f83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Tue, 16 May 2023 11:04:52 +0200 Subject: [PATCH 07/10] Fix header title --- .../native-image/guides/debuginfo-walkthrough.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference-manual/native-image/guides/debuginfo-walkthrough.md b/docs/reference-manual/native-image/guides/debuginfo-walkthrough.md index a0b59f562493..0fcee0ed06de 100644 --- a/docs/reference-manual/native-image/guides/debuginfo-walkthrough.md +++ b/docs/reference-manual/native-image/guides/debuginfo-walkthrough.md @@ -49,7 +49,7 @@ The following sections are available * .debug.svm.imagebuild.arguments * .debug.svm.imagebuild.java.properties -#### Where is the main function? +#### Where is the main method? Use ```text info functions ::main From 9f7bb9cd98d5aa3bc088cf6c4af498b6d505f9ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Tue, 16 May 2023 11:05:49 +0200 Subject: [PATCH 08/10] Explain `-g` with `-O0` in a more user-friendly way --- .../native-image/guides/debuginfo-walkthrough.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/reference-manual/native-image/guides/debuginfo-walkthrough.md b/docs/reference-manual/native-image/guides/debuginfo-walkthrough.md index 0fcee0ed06de..c5a61cf61c63 100644 --- a/docs/reference-manual/native-image/guides/debuginfo-walkthrough.md +++ b/docs/reference-manual/native-image/guides/debuginfo-walkthrough.md @@ -23,10 +23,9 @@ permalink: /reference-manual/native-image/guides/debuginfo-walkthrough/ sources ``` * GDB automatically loads the `.debug` file for a given native executable ``. (There is a link between the image and its `*.debug`-file) -* Combining `-g` with `-O0` generally gives a better debugging experience. Because `-O0` among other things - * restricts inlining (helps, because accessing params and locals in inlined methods does not work reliably) - * sets `GraphBuilderConfiguration#withRetainLocalVariables` - * enables `SubstrateOptions.SourceLevelDebug` which sets `GraphBuilderConfiguration.withFullInfopoints` +* 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. ### Using GDB with the new debuginfo From 14dd848784f3042ad5decb52d840419d9d65800e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Tue, 16 May 2023 09:08:37 +0000 Subject: [PATCH 09/10] Reword based on review feedback --- .../native-image/guides/debuginfo-walkthrough.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/reference-manual/native-image/guides/debuginfo-walkthrough.md b/docs/reference-manual/native-image/guides/debuginfo-walkthrough.md index c5a61cf61c63..2be93bd0ef90 100644 --- a/docs/reference-manual/native-image/guides/debuginfo-walkthrough.md +++ b/docs/reference-manual/native-image/guides/debuginfo-walkthrough.md @@ -207,7 +207,8 @@ $18 = { hub = 0x1d1cae0 }, }, } ``` -But we do have the `hub`-field that allows us to see that the runtime-type of the Greeter object at address `0x7ff7f9101208` is: +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 From 412afb48a9417f41a6d3bdc41107d7093a995d0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Tue, 16 May 2023 12:00:22 +0200 Subject: [PATCH 10/10] Replace contents of debug-native-executables-with-gdb.md with debuginfo-walkthrough.md --- .../debug-native-executables-with-gdb.md | 622 ++++++++++++++---- .../guides/debuginfo-walkthrough.md | 512 -------------- .../native-image/guides/guides.md | 1 - 3 files changed, 489 insertions(+), 646 deletions(-) delete mode 100644 docs/reference-manual/native-image/guides/debuginfo-walkthrough.md 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/docs/reference-manual/native-image/guides/debuginfo-walkthrough.md b/docs/reference-manual/native-image/guides/debuginfo-walkthrough.md deleted file mode 100644 index 2be93bd0ef90..000000000000 --- a/docs/reference-manual/native-image/guides/debuginfo-walkthrough.md +++ /dev/null @@ -1,512 +0,0 @@ ---- -layout: ni-docs -toc_group: how-to-guides -link_title: Debuginfo Generation Walkthrough -permalink: /reference-manual/native-image/guides/debuginfo-walkthrough/ ---- - -# Debuginfo Generation Walkthrough - -### Which GDB to use? - -* 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 - -### 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 - ``` - * 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. - -### Using GDB with the new debuginfo - -#### 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 - -#### 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, - ... - 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" -``` - -#### 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). - -#### Calling `svm_dbg_`-helper functions during debugging - -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: - -| Platform | Register | -| --------- | -------- | -| `amd64` | `$r15` | -| `aarch64` | `$r28` | - -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 -``` - -##### The following `svm_dbg_`-helper methods are currently defined: - -```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/docs/reference-manual/native-image/guides/guides.md b/docs/reference-manual/native-image/guides/guides.md index 0bd37b165479..7a126b52dc11 100644 --- a/docs/reference-manual/native-image/guides/guides.md +++ b/docs/reference-manual/native-image/guides/guides.md @@ -23,7 +23,6 @@ Here you will learn how to: - [Containerise a Native Executable and Run in a Docker Container](containerise-native-executable-with-docker.md) - [Create a Heap Dump from a Native Executable](create-heap-dump-from-native-executable.md) - [Debug Native Executables with GDB](debug-native-executables-with-gdb.md) -- [Debuginfo Generation Walkthrough](debuginfo-walkthrough.md) - [Include Resources in a Native Executable](include-resources.md) - [Optimize a Native Executable with Profile-Guided Optimizations](optimize-native-executable-with-pgo.md) - [Use GraalVM Dashboard to Optimize the Size of a Native Executable](use-graalvm-dashboard.md)