Skip to content

Commit 1a33260

Browse files
Rework debug information generation and add support for run-time debug information generation
Implement the GDB JIT compilation interface Split off a shared debug information generator for use at image build-time and run-time Rework debug entries Parallelize debug information generation Add LocalVariableTable to SubstrateMethod Add tests for run-time debug information generation Refactor and rework gdb-debughelpers Make BFDNameProvider usable at image run-time Update Line section to DWARF5, fix bug when loading debug info in recent GDB versions (>= GDB 16) Add support for lazy deoptimization for frame unwinder and frame filter Implement opaque type resolution for runtime debug information in GDB Updates to CV debug information to be compatible with reworked debug information generation
1 parent a8265d2 commit 1a33260

File tree

99 files changed

+8067
-6987
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

99 files changed

+8067
-6987
lines changed

substratevm/debug/gdbpy/gdb-debughelpers.py

Lines changed: 710 additions & 441 deletions
Large diffs are not rendered by default.
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
26+
#ifndef SVM_NATIVE_GDBJITCOMPILATIONINTERFACE_H
27+
#define SVM_NATIVE_GDBJITCOMPILATIONINTERFACE_H
28+
29+
// This header specifies the types used by the GDB JIT compilation interface (see https://sourceware.org/gdb/current/onlinedocs/gdb.html/Declarations.html#Declarations)
30+
// The implementation of the JIT compilation interface is located in com.oracle.svm.core.debug.GdbJitInterface.
31+
32+
#include <stdint.h>
33+
34+
typedef enum
35+
{
36+
JIT_NOACTION = 0,
37+
JIT_REGISTER,
38+
JIT_UNREGISTER
39+
} jit_actions_t;
40+
41+
struct jit_code_entry
42+
{
43+
struct jit_code_entry *next_entry;
44+
struct jit_code_entry *prev_entry;
45+
const char *symfile_addr;
46+
uint64_t symfile_size;
47+
};
48+
49+
struct jit_descriptor
50+
{
51+
uint32_t version;
52+
/* This type should be jit_actions_t, but we use uint32_t
53+
to be explicit about the bitwidth. */
54+
uint32_t action_flag;
55+
struct jit_code_entry *relevant_entry;
56+
struct jit_code_entry *first_entry;
57+
};
58+
59+
#endif

substratevm/mx.substratevm/gdb_utils.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ def check(self, text, skip_fails=True):
110110
for i in range(0, num_rexps):
111111
rexp = rexps[i]
112112
match = None
113+
if skip_fails:
114+
line_idx = 0
113115
while line_idx < num_lines and match is None:
114116
line = lines[line_idx]
115117
match = rexp.match(line)

substratevm/mx.substratevm/mx_substratevm.py

Lines changed: 116 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1045,6 +1045,20 @@ def build_debug_test(variant_name, image_name, extra_args):
10451045
mx.run([os.environ.get('GDB_BIN', 'gdb'), '--nx', '-q', '-iex', 'set pagination off', '-ex', 'python "ISOLATES=True"', '-x', testhello_py, hello_binary])
10461046

10471047

1048+
def gdb_base_command(logfile, autoload_path):
1049+
return [
1050+
os.environ.get('GDB_BIN', 'gdb'),
1051+
'--nx',
1052+
'-q', # do not print the introductory and copyright messages
1053+
'-iex', 'set pagination off', # messages from enabling logging could already cause pagination, so this must be done first
1054+
'-iex', 'set logging redirect on',
1055+
'-iex', 'set logging overwrite off',
1056+
'-iex', f"set logging file {logfile}",
1057+
'-iex', 'set logging enabled on',
1058+
'-iex', f"set auto-load safe-path {autoload_path}",
1059+
]
1060+
1061+
10481062
def _gdbdebughelperstest(native_image, path, with_isolates_only, args):
10491063

10501064
# ====== check gdb version ======
@@ -1094,15 +1108,6 @@ def _gdbdebughelperstest(native_image, path, with_isolates_only, args):
10941108
'com.oracle.svm.test.debug.helper.ClassLoaderTest'
10951109
]
10961110

1097-
gdb_args = [
1098-
os.environ.get('GDB_BIN', 'gdb'),
1099-
'--nx',
1100-
'-q', # do not print the introductory and copyright messages
1101-
'-iex', 'set pagination off', # messages from enabling logging could already cause pagination, so this must be done first
1102-
'-iex', 'set logging redirect on',
1103-
'-iex', 'set logging overwrite off',
1104-
]
1105-
11061111
def run_debug_test(image_name: str, testfile: str, source_path: str, with_isolates: bool = True,
11071112
build_cinterfacetutorial: bool = False, extra_args: list = None,
11081113
skip_build: bool = False) -> int:
@@ -1138,7 +1143,7 @@ def run_debug_test(image_name: str, testfile: str, source_path: str, with_isolat
11381143
else:
11391144
build_args += ['-o', join(build_dir, image_name)]
11401145

1141-
mx.log(f"native_image {' '.join(build_args)}")
1146+
mx.log(f"native-image {' '.join(build_args)}")
11421147
native_image(build_args)
11431148

11441149
if build_cinterfacetutorial:
@@ -1151,18 +1156,13 @@ def run_debug_test(image_name: str, testfile: str, source_path: str, with_isolat
11511156
else:
11521157
c_command = ['cl', '-MD', join(tutorial_c_source_dir, 'cinterfacetutorial.c'), '-I.',
11531158
'libcinterfacetutorial.lib']
1154-
mx.log(' '.join(c_command))
11551159
mx.run(c_command, cwd=build_dir)
11561160
if mx.get_os() == 'linux':
11571161
logfile = join(path, pathlib.Path(testfile).stem + ('' if with_isolates else '_no_isolates') + '.log')
1158-
os.environ.update({'gdbdebughelperstest_logfile': logfile})
1159-
gdb_command = gdb_args + [
1160-
'-iex', f"set logging file {logfile}",
1161-
'-iex', 'set logging enabled on',
1162-
'-iex', f"set auto-load safe-path {join(build_dir, 'gdb-debughelpers.py')}",
1162+
os.environ.update({'gdb_logfile': logfile})
1163+
gdb_command = gdb_base_command(logfile, join(build_dir, 'gdb-debughelpers.py')) + [
11631164
'-x', testfile, join(build_dir, image_name)
11641165
]
1165-
mx.log(' '.join(gdb_command))
11661166
# unittest may result in different exit code, nonZeroIsFatal ensures that we can go on with other test
11671167
return mx.run(gdb_command, cwd=build_dir, nonZeroIsFatal=False)
11681168
return 0
@@ -1189,6 +1189,82 @@ def run_debug_test(image_name: str, testfile: str, source_path: str, with_isolat
11891189
mx.abort(status)
11901190

11911191

1192+
def _runtimedebuginfotest(native_image, output_path, with_isolates_only, args=None):
1193+
"""Build and run the runtimedebuginfotest"""
1194+
1195+
args = [] if args is None else args
1196+
1197+
test_proj = mx.dependency('com.oracle.svm.test')
1198+
test_source_path = test_proj.source_dirs()[0]
1199+
1200+
test_python_source_dir = join(test_source_path, 'com', 'oracle', 'svm', 'test', 'debug', 'helper')
1201+
test_runtime_compilation_py = join(test_python_source_dir, 'test_runtime_compilation.py')
1202+
test_runtime_deopt_py = join(test_python_source_dir, 'test_runtime_deopt.py')
1203+
testdeopt_js = join(suite.dir, 'mx.substratevm', 'testdeopt.js')
1204+
1205+
# clean / create output directory
1206+
if exists(output_path):
1207+
mx.rmtree(output_path)
1208+
mx_util.ensure_dir_exists(output_path)
1209+
1210+
# Build the native image from Java code
1211+
build_args = [
1212+
'-g', '-O0',
1213+
# set property controlling inclusion of foreign struct header
1214+
'-DbuildDebugInfoTestExample=true',
1215+
'--native-compiler-options=-I' + test_source_path,
1216+
'-o', join(output_path, 'runtimedebuginfotest'),
1217+
'-cp', classpath('com.oracle.svm.test'),
1218+
# We do not want to step into class initializer, so initialize everything at build time.
1219+
'--initialize-at-build-time=com.oracle.svm.test.debug.helper',
1220+
'--features=com.oracle.svm.test.debug.helper.RuntimeCompileDebugInfoTest$RegisterMethodsFeature',
1221+
'com.oracle.svm.test.debug.helper.RuntimeCompileDebugInfoTest',
1222+
] + svm_experimental_options([
1223+
'-H:DebugInfoSourceSearchPath=' + test_source_path,
1224+
'-H:+SourceLevelDebug',
1225+
'-H:+RuntimeDebugInfo',
1226+
]) + args
1227+
1228+
mx.log(f"native-image {' '.join(build_args)}")
1229+
runtime_compile_binary = native_image(build_args)
1230+
1231+
logfile = join(output_path, 'test_runtime_compilation.log')
1232+
os.environ.update({'gdb_logfile': logfile})
1233+
gdb_command = gdb_base_command(logfile, join(output_path, 'gdb-debughelpers.py')) + [
1234+
'-x', test_runtime_compilation_py, runtime_compile_binary
1235+
]
1236+
# unittest may result in different exit code, nonZeroIsFatal ensures that we can go on with other test
1237+
status = mx.run(gdb_command, cwd=output_path, nonZeroIsFatal=False)
1238+
1239+
def run_js_test(eager: bool = False):
1240+
jslib = mx.add_lib_suffix(native_image(
1241+
args +
1242+
svm_experimental_options([
1243+
'-H:+SourceLevelDebug',
1244+
'-H:+RuntimeDebugInfo',
1245+
'-H:+LazyDeoptimization' if eager else '-H:-LazyDeoptimization',
1246+
]) +
1247+
['-g', '-O0', '--macro:jsvm-library']
1248+
))
1249+
js_launcher = get_js_launcher(jslib)
1250+
logfile = join(output_path, 'test_runtime_deopt_' + ('eager' if eager else 'lazy') + '.log')
1251+
os.environ.update({'gdb_logfile': logfile})
1252+
gdb_command = gdb_base_command(logfile, join(output_path, 'gdb-debughelpers.py')) + [
1253+
'-x', test_runtime_deopt_py, '--args', js_launcher, testdeopt_js
1254+
]
1255+
# unittest may result in different exit code, nonZeroIsFatal ensures that we can go on with other test
1256+
return mx.run(gdb_command, cwd=output_path, nonZeroIsFatal=False)
1257+
1258+
# G1 does not work for the jsvm library
1259+
# avoid complications with '-H:+ProtectionKeys' which is not compatible with '-H:-SpawnIsolates' and '-H:-UseCompressedReferences'
1260+
if '--gc=G1' not in args and '-H:-UseCompressedReferences' not in args and '-H:-SpawnIsolates' not in args:
1261+
status |= run_js_test()
1262+
status |= run_js_test(True)
1263+
1264+
if status != 0:
1265+
mx.abort(status)
1266+
1267+
11921268
def _javac_image(native_image, path, args=None):
11931269
args = [] if args is None else args
11941270
mx_util.ensure_dir_exists(path)
@@ -1766,6 +1842,28 @@ def gdbdebughelperstest(args, config=None):
17661842
config=config
17671843
)
17681844

1845+
1846+
@mx.command(suite.name, 'runtimedebuginfotest', 'Runs the runtime debuginfo generation test')
1847+
def runtimedebuginfotest(args, config=None):
1848+
"""
1849+
runs a native image that compiles code and creates debug info at runtime.
1850+
"""
1851+
parser = ArgumentParser(prog='mx runtimedebuginfotest')
1852+
all_args = ['--output-path', '--with-isolates-only']
1853+
masked_args = [_mask(arg, all_args) for arg in args]
1854+
parser.add_argument(all_args[0], metavar='<output-path>', nargs=1, help='Path of the generated image', default=[join(svmbuild_dir(), "runtimedebuginfotest")])
1855+
parser.add_argument(all_args[1], action='store_true', help='Only build and test the native image with isolates')
1856+
parser.add_argument('image_args', nargs='*', default=[])
1857+
parsed = parser.parse_args(masked_args)
1858+
output_path = unmask(parsed.output_path)[0]
1859+
with_isolates_only = parsed.with_isolates_only
1860+
native_image_context_run(
1861+
lambda native_image, a:
1862+
_runtimedebuginfotest(native_image, output_path, with_isolates_only, a), unmask(parsed.image_args),
1863+
config=config
1864+
)
1865+
1866+
17691867
@mx.command(suite_name=suite.name, command_name='helloworld', usage_msg='[options]')
17701868
def helloworld(args, config=None):
17711869
"""
@@ -1858,6 +1956,7 @@ def build_and_test_java_agent_image(native_image, args):
18581956

18591957
native_image_context_run(build_and_test_java_agent_image, args)
18601958

1959+
18611960
@mx.command(suite.name, 'clinittest', 'Runs the ')
18621961
def clinittest(args):
18631962
def build_and_test_clinittest_image(native_image, args):

substratevm/mx.substratevm/suite.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,7 @@
361361
"dependencies": [
362362
"com.oracle.svm.common",
363363
"com.oracle.svm.shaded.org.objectweb.asm",
364+
"com.oracle.objectfile",
364365
"SVM_CONFIGURE",
365366
"espresso-shared:ESPRESSO_SVM",
366367
],
@@ -738,7 +739,6 @@
738739
"subDir": "src",
739740
"sourceDirs": ["src"],
740741
"dependencies": [
741-
"com.oracle.objectfile",
742742
"com.oracle.graal.reachability",
743743
"com.oracle.svm.core.graal.amd64",
744744
"com.oracle.svm.shaded.org.capnproto",
@@ -1152,6 +1152,10 @@
11521152
"jdk.internal.misc",
11531153
"sun.security.jca",
11541154
],
1155+
"jdk.internal.vm.ci" : [
1156+
"jdk.vm.ci.code",
1157+
"jdk.vm.ci.meta",
1158+
],
11551159
},
11561160
"checkstyle": "com.oracle.svm.test",
11571161
"checkstyleVersion" : "10.21.0",
@@ -1971,6 +1975,8 @@
19711975
"com.oracle.objectfile",
19721976
"com.oracle.objectfile.io",
19731977
"com.oracle.objectfile.debuginfo",
1978+
"com.oracle.objectfile.debugentry",
1979+
"com.oracle.objectfile.debugentry.range",
19741980
"com.oracle.objectfile.macho",
19751981
],
19761982

@@ -2101,6 +2107,7 @@
21012107
"dependency:com.oracle.svm.native.libchelper/*",
21022108
"dependency:com.oracle.svm.native.jvm.posix/*",
21032109
"dependency:com.oracle.svm.native.libcontainer/*",
2110+
"file:debug/include",
21042111
],
21052112
},
21062113
},
@@ -2109,6 +2116,7 @@
21092116
# on all other os's we don't want libc specific subdirectories
21102117
"include/": [
21112118
"dependency:com.oracle.svm.native.libchelper/include/*",
2119+
"file:debug/include/*",
21122120
],
21132121
"<os>-<arch>/": [
21142122
"dependency:com.oracle.svm.native.libchelper/<os>-<arch>/default/*",
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
26+
function add(a, b, test) {
27+
if (test) {
28+
a += b;
29+
}
30+
return a + b;
31+
}
32+
33+
// trigger compilation add for ints and test = true
34+
for (let i = 0; i < 1000 * 1000; i++) {
35+
add(i, i, true);
36+
}
37+
38+
// deoptimize with failed assumption in compiled method
39+
// then trigger compilation again
40+
console.log("deopt1")
41+
for (let i = 0; i < 1000 * 1000; i++) {
42+
add(i, i, false);
43+
}
44+
45+
// deoptimize with different parameter types
46+
console.log("deopt2");
47+
add({f1: "test1", f2: 2}, {x: "x", y: {test: 42}}, false);

0 commit comments

Comments
 (0)