Skip to content

Commit d45cb82

Browse files
[GR-67238] Provide layered image build variant of debuginfotests.
PullRequest: graal/21435
2 parents a931870 + a019ee9 commit d45cb82

File tree

27 files changed

+356
-169
lines changed

27 files changed

+356
-169
lines changed

substratevm/mx.substratevm/mx_substratevm.py

Lines changed: 161 additions & 82 deletions
Large diffs are not rendered by default.

substratevm/mx.substratevm/suite.py

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1120,10 +1120,6 @@
11201120
"jdk.internal.misc",
11211121
"sun.security.jca",
11221122
],
1123-
"jdk.internal.vm.ci" : [
1124-
"jdk.vm.ci.code",
1125-
"jdk.vm.ci.meta",
1126-
],
11271123
},
11281124
"checkstyle": "com.oracle.svm.test",
11291125
"checkstyleVersion" : "10.21.0",
@@ -1137,19 +1133,46 @@
11371133
"jacoco" : "exclude",
11381134
},
11391135

1140-
"com.oracle.svm.test.missing.classes": {
1136+
"com.oracle.svm.test.debug": {
11411137
"subDir": "src",
11421138
"sourceDirs": ["src"],
11431139
"dependencies": [
1140+
"sdk:NATIVEIMAGE",
1141+
"SVM",
11441142
],
1143+
"requiresConcealed": {
1144+
"jdk.internal.vm.ci": [
1145+
"jdk.vm.ci.code",
1146+
"jdk.vm.ci.meta",
1147+
],
1148+
},
1149+
"checkstyle": "com.oracle.svm.test",
1150+
"workingSets": "SVM",
11451151
"annotationProcessors": [
1152+
"compiler:GRAAL_PROCESSOR",
1153+
"SVM_PROCESSOR",
1154+
],
1155+
"javaCompliance": "21+",
1156+
"spotbugs": "false",
1157+
"jacoco": "exclude",
1158+
"testProject": True,
1159+
},
1160+
1161+
"com.oracle.svm.test.debug.missing.classes": {
1162+
"subDir": "src",
1163+
"sourceDirs": ["src"],
1164+
"dependencies": [
11461165
],
11471166
"checkstyle": "com.oracle.svm.test",
1148-
"javaCompliance" : "21+",
11491167
"workingSets": "SVM",
1168+
"annotationProcessors": [
1169+
"compiler:GRAAL_PROCESSOR",
1170+
"SVM_PROCESSOR",
1171+
],
1172+
"javaCompliance": "21+",
11501173
"spotbugs": "false",
1174+
"jacoco": "exclude",
11511175
"testProject": True,
1152-
"jacoco" : "exclude",
11531176
},
11541177

11551178
"com.oracle.svm.with.space.test": {
@@ -2437,27 +2460,19 @@
24372460
"com.oracle.svm.test",
24382461
"com.oracle.svm.configure.test",
24392462
"com.oracle.svm.graal.test",
2463+
"com.oracle.svm.test.debug",
2464+
"com.oracle.svm.test.debug.missing.classes",
24402465
"SVM_TEST_RESOURCE_WITH_SPACE",
24412466
],
24422467
"distDependencies": [
24432468
"mx:JUNIT_TOOL",
24442469
"sdk:NATIVEIMAGE",
24452470
"SVM",
24462471
"SVM_CONFIGURE",
2447-
"SVM_TEST_MISSING_CLASSES",
24482472
],
24492473
"testDistribution" : True,
24502474
},
24512475

2452-
"SVM_TEST_MISSING_CLASSES" : {
2453-
"subDir": "src",
2454-
"relpath" : True,
2455-
"dependencies": [
2456-
"com.oracle.svm.test.missing.classes"
2457-
],
2458-
"testDistribution": True,
2459-
},
2460-
24612476
# Special test distribution used for testing inclusion of resources from jar files with a space in their name.
24622477
# The space in the distribution name is intentional.
24632478
"SVM_TESTS WITH SPACE" : {

substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/MethodEntry.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,18 +48,20 @@ public record Local(LocalEntry localEntry, int line) {
4848
private boolean isInlined;
4949
private final boolean isOverride;
5050
private final boolean isConstructor;
51+
private final boolean isCompiledInPriorLayer;
5152
private final int vtableOffset;
5253
private final String symbolName;
5354

5455
public MethodEntry(FileEntry fileEntry, int line, String methodName, StructureTypeEntry ownerType,
5556
TypeEntry valueType, int modifiers, List<LocalEntry> params, String symbolName, boolean isDeopt, boolean isOverride, boolean isConstructor,
56-
int vtableOffset, int lastParamSlot) {
57+
boolean isCompiledInPriorLayer, int vtableOffset, int lastParamSlot) {
5758
super(fileEntry, line, methodName, ownerType, valueType, modifiers);
5859
this.params = params;
5960
this.symbolName = symbolName;
6061
this.isDeopt = isDeopt;
6162
this.isOverride = isOverride;
6263
this.isConstructor = isConstructor;
64+
this.isCompiledInPriorLayer = isCompiledInPriorLayer;
6365
this.vtableOffset = vtableOffset;
6466
this.lastParamSlot = lastParamSlot;
6567
this.locals = new ArrayList<>();
@@ -208,6 +210,10 @@ public boolean isConstructor() {
208210
return isConstructor;
209211
}
210212

213+
public boolean isCompiledInPriorLayer() {
214+
return isCompiledInPriorLayer;
215+
}
216+
211217
public boolean isVirtual() {
212218
return vtableOffset >= 0;
213219
}

substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -605,7 +605,7 @@ private int writeInstanceClasses(DebugContext context, byte[] buffer, int p) {
605605
* declaration in the owner type CU. Other information is already written to the
606606
* corresponding type units.
607607
*/
608-
if (classEntry.getMethods().stream().anyMatch(m -> m.isInRange() || m.isInlined()) ||
608+
if (classEntry.getMethods().stream().anyMatch(m -> m.isInRange() || m.isInlined() || m.isCompiledInPriorLayer()) ||
609609
classEntry.getFields().stream().anyMatch(DwarfInfoSectionImpl::isManifestedStaticField)) {
610610
setCUIndex(classEntry, pos);
611611
pos = writeInstanceClassInfo(context, classEntry, buffer, pos);
@@ -1279,7 +1279,7 @@ private int writeField(DebugContext context, StructureTypeEntry entry, FieldEntr
12791279
private int writeSkeletonMethodDeclarations(DebugContext context, ClassEntry classEntry, byte[] buffer, int p) {
12801280
int pos = p;
12811281
for (MethodEntry method : classEntry.getMethods()) {
1282-
if (method.isInRange() || method.isInlined()) {
1282+
if (method.isInRange() || method.isInlined() || method.isCompiledInPriorLayer()) {
12831283
/*
12841284
* Declare all methods whether or not they have been compiled or inlined.
12851285
*/
@@ -1357,7 +1357,7 @@ private int writeSkeletonMethodParameterDeclaration(DebugContext context, LocalE
13571357
private int writeMethodDeclarations(DebugContext context, ClassEntry classEntry, byte[] buffer, int p) {
13581358
int pos = p;
13591359
for (MethodEntry method : classEntry.getMethods()) {
1360-
if (method.isInRange() || method.isInlined()) {
1360+
if (method.isInRange() || method.isInlined() || method.isCompiledInPriorLayer()) {
13611361
/*
13621362
* Declare all methods whether they have been compiled or inlined.
13631363
*/

substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfLineSectionImpl.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,11 @@ private int writeCompiledMethodLineInfo(DebugContext context, ClassEntry classEn
508508
pos = writeAdvancePCOp(context, addressDelta, buffer, pos);
509509
}
510510
}
511+
/*
512+
* Add a row to the line number table and reset some flags. This makes sure a
513+
* debugger can stop here and properly process line and address. Special opcodes
514+
* have a similar effect as the copy operation, hence it is only used here.
515+
*/
511516
pos = writeCopyOp(context, buffer, pos);
512517
}
513518
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/debug/SharedDebugInfoProvider.java

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -733,10 +733,11 @@ private MethodEntry createMethodEntry(SharedMethod method) {
733733
boolean isOverride = isOverride(method);
734734
boolean isDeopt = method.isDeoptTarget();
735735
boolean isConstructor = method.isConstructor();
736+
boolean isCompiledInPriorLayer = isCompiledInPriorLayer(method);
736737

737738
return new MethodEntry(fileEntry, line, methodName, ownerType,
738739
valueType, modifiers, params, symbolName, isDeopt, isOverride, isConstructor,
739-
vTableOffset, lastParamSlot);
740+
isCompiledInPriorLayer, vTableOffset, lastParamSlot);
740741
}
741742

742743
/**
@@ -1010,6 +1011,10 @@ public boolean isOverride(@SuppressWarnings("unused") SharedMethod method) {
10101011
return false;
10111012
}
10121013

1014+
public boolean isCompiledInPriorLayer(@SuppressWarnings("unused") SharedMethod method) {
1015+
return false;
1016+
}
1017+
10131018
public boolean isVirtual(@SuppressWarnings("unused") SharedMethod method) {
10141019
return false;
10151020
}
@@ -1217,7 +1222,7 @@ protected LocalEntry lookupLocalEntry(String name, TypeEntry typeEntry, int slot
12171222
/**
12181223
* Lookup a {@code LocalValueEntry}. This can either be a register, stack, constant value. This
12191224
* allows to reuse the same entries for multiple ranges.
1220-
*
1225+
*
12211226
* @param localValueEntry the {@code LocalValueEntry} to lookup
12221227
* @return the {@code LocalValueEntry} from the lookup map
12231228
*/
@@ -1403,7 +1408,7 @@ private Map<LocalEntry, LocalValueEntry> initSyntheticInfoList(ParamLocationProd
14031408
debug.log(DebugContext.DETAILED_LEVEL, "local[%d] %s type %s slot %d", paramIdx + 1, param.name(), param.type().getTypeName(), param.slot());
14041409
debug.log(DebugContext.DETAILED_LEVEL, " => %s", value);
14051410
}
1406-
LocalValueEntry localValueEntry = createLocalValueEntry(value, PRE_EXTEND_FRAME_SIZE);
1411+
LocalValueEntry localValueEntry = createLocalValueEntry(value, PRE_EXTEND_FRAME_SIZE, true);
14071412
if (localValueEntry != null) {
14081413
localValueInfos.put(param, localValueEntry);
14091414
}
@@ -1565,7 +1570,13 @@ public Range process(CompilationResultFrameTree.FrameNode node, CallRange caller
15651570
LineNumberTable lineNumberTable = method.getLineNumberTable();
15661571
int line = lineNumberTable == null ? -1 : lineNumberTable.getLineNumber(pos.getBCI());
15671572

1568-
Map<LocalEntry, LocalValueEntry> localValueInfos = initLocalInfoList(pos, methodEntry, frameSize);
1573+
/*
1574+
* Locals are stored in both leaf ranges and call ranges. However, for call ranges we do
1575+
* not want to store register values as registers may be overwritten in callee ranges.
1576+
* Thus, a debugger is still able to process stack values and constants for stack
1577+
* traces.
1578+
*/
1579+
Map<LocalEntry, LocalValueEntry> localValueInfos = initLocalInfoList(pos, methodEntry, frameSize, isLeaf);
15691580
Range locationInfo = Range.createSubrange(primary, methodEntry, localValueInfos, node.getStartPos(), node.getEndPos() + 1, line, callerInfo, isLeaf);
15701581

15711582
if (debug.isLogEnabled()) {
@@ -1585,9 +1596,10 @@ public Range process(CompilationResultFrameTree.FrameNode node, CallRange caller
15851596
* @param pos the bytecode position of the location info
15861597
* @param methodEntry the {@code MethodEntry} corresponding to the bytecode position
15871598
* @param frameSize the current frame size
1599+
* @param isLeaf whether the range to create this value for is a leaf range
15881600
* @return a mapping from {@code LocalEntry} to {@code LocalValueEntry}
15891601
*/
1590-
protected Map<LocalEntry, LocalValueEntry> initLocalInfoList(BytecodePosition pos, MethodEntry methodEntry, int frameSize) {
1602+
protected Map<LocalEntry, LocalValueEntry> initLocalInfoList(BytecodePosition pos, MethodEntry methodEntry, int frameSize, boolean isLeaf) {
15911603
Map<LocalEntry, LocalValueEntry> localInfos = new HashMap<>();
15921604

15931605
if (pos instanceof BytecodeFrame frame && frame.numLocals > 0) {
@@ -1688,7 +1700,7 @@ protected Map<LocalEntry, LocalValueEntry> initLocalInfoList(BytecodePosition po
16881700
* upfront from the local variable table, the LocalEntry already exists.
16891701
*/
16901702
LocalEntry localEntry = methodEntry.getOrAddLocalEntry(lookupLocalEntry(name, typeEntry, slot), line);
1691-
LocalValueEntry localValueEntry = createLocalValueEntry(value, frameSize);
1703+
LocalValueEntry localValueEntry = createLocalValueEntry(value, frameSize, isLeaf);
16921704
if (localEntry != null && localValueEntry != null) {
16931705
localInfos.put(localEntry, localValueEntry);
16941706
}
@@ -1703,16 +1715,24 @@ protected Map<LocalEntry, LocalValueEntry> initLocalInfoList(BytecodePosition po
17031715

17041716
/**
17051717
* Creates a {@code LocalValueEntry} for a given {@code JavaValue}. This processes register
1706-
* values, stack values, primitive constants and constant in the heap.
1718+
* values, stack values, primitive constants and constant in the heap. Register values are only
1719+
* added for leaf frames, as register may be overwritten within callee frames of a call range.
1720+
* But, stack values and constants also work for call ranges.
17071721
*
17081722
* @param value the given {@code JavaValue}
17091723
* @param frameSize the frame size for stack values
1724+
* @param isLeaf whether the range to create this value for is a leaf range
17101725
* @return the {@code LocalValueEntry} or {@code null} if the value can't be processed
17111726
*/
1712-
private LocalValueEntry createLocalValueEntry(JavaValue value, int frameSize) {
1727+
private LocalValueEntry createLocalValueEntry(JavaValue value, int frameSize, boolean isLeaf) {
17131728
switch (value) {
17141729
case RegisterValue registerValue -> {
1715-
return lookupLocalValueEntry(new RegisterValueEntry(registerValue.getRegister().number));
1730+
/*
1731+
* We only want to create register entries for leaf nodes. Thus, they are only valid
1732+
* within a leaf range but not over a whole call range where the register value is
1733+
* potentially overwritten.
1734+
*/
1735+
return isLeaf ? lookupLocalValueEntry(new RegisterValueEntry(registerValue.getRegister().number)) : null;
17161736
}
17171737
case StackSlot stackValue -> {
17181738
int stackSlot = frameSize == 0 ? stackValue.getRawOffset() : stackValue.getOffset(frameSize);
@@ -1827,7 +1847,7 @@ private Range createEmbeddedParentLocationInfo(PrimaryRange primary, Compilation
18271847
LineNumberTable lineNumberTable = method.getLineNumberTable();
18281848
int line = lineNumberTable == null ? -1 : lineNumberTable.getLineNumber(pos.getBCI());
18291849

1830-
Map<LocalEntry, LocalValueEntry> localValueInfos = initLocalInfoList(pos, methodEntry, frameSize);
1850+
Map<LocalEntry, LocalValueEntry> localValueInfos = initLocalInfoList(pos, methodEntry, frameSize, true);
18311851
Range locationInfo = Range.createSubrange(primary, methodEntry, localValueInfos, startPos, endPos, line, callerLocation, true);
18321852

18331853
if (debug.isLogEnabled()) {
@@ -1881,7 +1901,7 @@ private static boolean skipPos(BytecodePosition pos) {
18811901
* at native image build-time while still having comparable performance (for most of the index
18821902
* maps). The most performance critical maps that see more traffic will use the less memory
18831903
* efficient ConcurrentHashMaps instead.
1884-
*
1904+
*
18851905
* @param map the {@code EconomicMap} to perform {@code computeIfAbsent} on
18861906
* @param key the key to look for
18871907
* @param mappingFunction the function producing the value for a given key

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@
7171
import com.oracle.svm.core.debug.SubstrateDebugTypeEntrySupport;
7272
import com.oracle.svm.core.graal.meta.RuntimeConfiguration;
7373
import com.oracle.svm.core.image.ImageHeapPartition;
74+
import com.oracle.svm.core.imagelayer.DynamicImageLayerInfo;
75+
import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport;
7476
import com.oracle.svm.core.meta.SharedMethod;
7577
import com.oracle.svm.core.meta.SharedType;
7678
import com.oracle.svm.core.util.VMError;
@@ -120,6 +122,8 @@ class NativeImageDebugInfoProvider extends SharedDebugInfoProvider {
120122
private final int primitiveStartOffset;
121123
private final int referenceStartOffset;
122124
private final Set<HostedMethod> allOverrides;
125+
private final boolean buildingImageLayer;
126+
private final int layerNumber;
123127

124128
NativeImageDebugInfoProvider(DebugContext debug, NativeImageCodeCache codeCache, NativeImageHeap heap, NativeLibraries nativeLibs, HostedMetaAccess metaAccess,
125129
RuntimeConfiguration runtimeConfiguration) {
@@ -141,6 +145,9 @@ class NativeImageDebugInfoProvider extends SharedDebugInfoProvider {
141145
.flatMap(m -> Arrays.stream(m.getImplementations())
142146
.filter(Predicate.not(m::equals)))
143147
.collect(Collectors.toSet());
148+
149+
buildingImageLayer = ImageLayerBuildingSupport.buildingImageLayer();
150+
layerNumber = DynamicImageLayerInfo.getCurrentLayerNumber();
144151
}
145152

146153
@SuppressWarnings("unused")
@@ -496,6 +503,11 @@ public boolean isOverride(SharedMethod method) {
496503
return method instanceof HostedMethod && allOverrides.contains(method);
497504
}
498505

506+
@Override
507+
public boolean isCompiledInPriorLayer(SharedMethod method) {
508+
return method instanceof HostedMethod hostedMethod && hostedMethod.isCompiledInPriorLayer();
509+
}
510+
499511
@Override
500512
public boolean isVirtual(SharedMethod method) {
501513
return method instanceof HostedMethod hostedMethod && hostedMethod.hasVTableIndex();
@@ -582,8 +594,14 @@ private void processFieldEntries(HostedType type, StructureTypeEntry structureTy
582594
}
583595

584596
for (ResolvedJavaField field : type.getStaticFields()) {
585-
assert field instanceof HostedField;
586-
structureTypeEntry.addField(createFieldEntry((HostedField) field, structureTypeEntry));
597+
HostedField hField = (HostedField) field;
598+
/*
599+
* If we are in a layered build only add debug info for a static field if it is
600+
* installed in the current layer.
601+
*/
602+
if (!buildingImageLayer || (hField.hasInstalledLayerNum() && layerNumber == hField.getInstalledLayerNum())) {
603+
structureTypeEntry.addField(createFieldEntry(hField, structureTypeEntry));
604+
}
587605
}
588606
}
589607

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
* or visit www.oracle.com if you need additional information or have any
2323
* questions.
2424
*/
25-
package com.oracle.svm.test.missing.classes;
25+
package com.oracle.svm.test.debug.missing.classes;
2626

2727
public class TestClass {
2828
public static final Object CONSTANT_OBJECT_FIELD = 42;
File renamed without changes.

substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/debug/CStructTests.java renamed to substratevm/src/com.oracle.svm.test.debug/src/com/oracle/svm/test/debug/CStructTests.java

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,10 @@
3636
import org.graalvm.nativeimage.c.type.CTypeConversion.CCharPointerHolder;
3737
import org.graalvm.word.PointerBase;
3838
import org.graalvm.word.SignedWord;
39+
import org.graalvm.word.WordBase;
3940

4041
import com.oracle.svm.core.NeverInline;
4142

42-
import jdk.graal.compiler.api.directives.GraalDirectives;
43-
4443
@CContext(CInterfaceDebugTestDirectives.class)
4544
public class CStructTests {
4645
@CPointerTo(nameOfCType = "int")
@@ -238,10 +237,20 @@ private static void testMixedArguments(String m1, short s, SimpleStruct ss1, lon
238237
System.out.println("You find " + m1);
239238
System.out.println("You find " + m2);
240239
System.out.println("You find " + m3);
241-
GraalDirectives.blackhole(s);
242-
GraalDirectives.blackhole(ss1);
243-
GraalDirectives.blackhole(l);
244-
GraalDirectives.blackhole(ss2);
240+
blackhole(s);
241+
blackhole(ss1);
242+
blackhole(l);
243+
blackhole(ss2);
244+
}
245+
246+
@NeverInline("For testing.")
247+
@SuppressWarnings("unused")
248+
static void blackhole(Object value) {
249+
}
250+
251+
@NeverInline("For testing.")
252+
@SuppressWarnings("unused")
253+
static void blackhole(WordBase value) {
245254
}
246255

247256
public static void main(String[] args) {

0 commit comments

Comments
 (0)