Skip to content

Commit 0a62f19

Browse files
committed
[GR-41279] Enhance reason backtrace.
PullRequest: graal/12900
2 parents 083e7cf + f65d849 commit 0a62f19

File tree

27 files changed

+432
-180
lines changed

27 files changed

+432
-180
lines changed

compiler/src/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/GraphKit.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -239,13 +239,22 @@ public ResolvedJavaMethod findMethod(Class<?> declaringClass, String name, Class
239239
}
240240
}
241241

242+
private NodeSourcePosition invokePosition(int invokeBci) {
243+
if (graph.trackNodeSourcePosition()) {
244+
NodeSourcePosition currentPosition = graph.currentNodeSourcePosition();
245+
assert currentPosition.getCaller() == null : "The GraphKit currentPosition should be a top level position.";
246+
return NodeSourcePosition.substitution(currentPosition.getMethod(), invokeBci);
247+
}
248+
return null;
249+
}
250+
242251
/**
243252
* Creates and appends an {@link InvokeNode} for a call to a given method with a given set of
244253
* arguments.
245254
*/
246255
@SuppressWarnings("try")
247256
public InvokeNode createInvoke(ResolvedJavaMethod method, InvokeKind invokeKind, FrameStateBuilder frameStateBuilder, int bci, ValueNode... args) {
248-
try (DebugCloseable context = graph.withNodeSourcePosition(NodeSourcePosition.substitution(graph.currentNodeSourcePosition(), method))) {
257+
try (DebugCloseable context = graph.withNodeSourcePosition(invokePosition(bci))) {
249258
assert method.isStatic() == (invokeKind == InvokeKind.Static);
250259
Signature signature = method.getSignature();
251260
JavaType returnType = signature.getReturnType(null);
@@ -282,7 +291,7 @@ public InvokeNode createIntrinsicInvoke(ResolvedJavaMethod method, ValueNode...
282291
@SuppressWarnings("try")
283292
public InvokeWithExceptionNode createInvokeWithExceptionAndUnwind(ResolvedJavaMethod method, InvokeKind invokeKind,
284293
FrameStateBuilder frameStateBuilder, int invokeBci, ValueNode... args) {
285-
try (DebugCloseable context = graph.withNodeSourcePosition(NodeSourcePosition.substitution(graph.currentNodeSourcePosition(), method))) {
294+
try (DebugCloseable context = graph.withNodeSourcePosition(invokePosition(invokeBci))) {
286295
InvokeWithExceptionNode result = startInvokeWithException(method, invokeKind, frameStateBuilder, invokeBci, args);
287296
exceptionPart();
288297
ExceptionObjectNode exception = exceptionObject();
@@ -294,7 +303,7 @@ public InvokeWithExceptionNode createInvokeWithExceptionAndUnwind(ResolvedJavaMe
294303

295304
@SuppressWarnings("try")
296305
public InvokeWithExceptionNode createInvokeWithExceptionAndUnwind(MethodCallTargetNode callTarget, FrameStateBuilder frameStateBuilder, int invokeBci) {
297-
try (DebugCloseable context = graph.withNodeSourcePosition(NodeSourcePosition.substitution(graph.currentNodeSourcePosition(), callTarget.targetMethod()))) {
306+
try (DebugCloseable context = graph.withNodeSourcePosition(invokePosition(invokeBci))) {
298307
InvokeWithExceptionNode result = startInvokeWithException(callTarget, frameStateBuilder, invokeBci);
299308
exceptionPart();
300309
ExceptionObjectNode exception = exceptionObject();

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/AbstractAnalysisEngine.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@
3535
import org.graalvm.compiler.debug.DebugContext.Builder;
3636
import org.graalvm.compiler.debug.DebugHandlersFactory;
3737
import org.graalvm.compiler.debug.Indent;
38+
import org.graalvm.compiler.graph.Node;
39+
import org.graalvm.compiler.nodes.DeoptBciSupplier;
40+
import org.graalvm.compiler.nodes.ValueNode;
3841
import org.graalvm.compiler.nodes.spi.Replacements;
3942
import org.graalvm.compiler.options.OptionValues;
4043
import org.graalvm.compiler.printer.GraalDebugHandlersFactory;
@@ -55,7 +58,10 @@
5558
import com.oracle.graal.pointsto.util.Timer;
5659
import com.oracle.graal.pointsto.util.TimerCollection;
5760

61+
import jdk.vm.ci.code.BytecodeFrame;
62+
import jdk.vm.ci.code.BytecodePosition;
5863
import jdk.vm.ci.meta.ConstantReflectionProvider;
64+
import jdk.vm.ci.meta.ResolvedJavaMethod;
5965

6066
/**
6167
* This abstract class is shared between Reachability and Points-to. It contains generic methods
@@ -307,4 +313,30 @@ public final boolean executorIsStarted() {
307313
public Replacements getReplacements() {
308314
return replacements;
309315
}
316+
317+
/**
318+
* Provide a non-null position. Some flows like newInstance and invoke require a non-null
319+
* position, for others is just better. The constructed position is best-effort, i.e., it
320+
* contains at least the method, and a BCI only if the node provides it.
321+
* <p>
322+
* This is necessary because {@link Node#getNodeSourcePosition()} doesn't always provide a
323+
* position, like for example for generated factory methods in FactoryThrowMethodHolder.
324+
*/
325+
public static BytecodePosition sourcePosition(ValueNode node) {
326+
BytecodePosition position = node.getNodeSourcePosition();
327+
if (position == null) {
328+
position = syntheticSourcePosition(node, node.graph().method());
329+
}
330+
return position;
331+
}
332+
333+
/** Creates a synthetic position for the node in the given method. */
334+
public static BytecodePosition syntheticSourcePosition(ValueNode node, ResolvedJavaMethod method) {
335+
int bci = BytecodeFrame.UNKNOWN_BCI;
336+
if (node instanceof DeoptBciSupplier) {
337+
bci = ((DeoptBciSupplier) node).bci();
338+
}
339+
return new BytecodePosition(null, method, bci);
340+
}
341+
310342
}

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanner.java

Lines changed: 113 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,21 @@
3737
import org.graalvm.word.WordBase;
3838

3939
import com.oracle.graal.pointsto.constraints.UnsupportedFeatureException;
40+
import com.oracle.graal.pointsto.heap.HeapSnapshotVerifier;
4041
import com.oracle.graal.pointsto.heap.ImageHeapArray;
4142
import com.oracle.graal.pointsto.heap.ImageHeapConstant;
43+
import com.oracle.graal.pointsto.heap.ImageHeapScanner;
4244
import com.oracle.graal.pointsto.meta.AnalysisField;
4345
import com.oracle.graal.pointsto.meta.AnalysisMethod;
4446
import com.oracle.graal.pointsto.meta.AnalysisType;
47+
import com.oracle.graal.pointsto.reports.ReportUtils;
4548
import com.oracle.graal.pointsto.util.AnalysisError;
4649
import com.oracle.graal.pointsto.util.CompletionExecutor;
4750

4851
import jdk.vm.ci.code.BytecodePosition;
4952
import jdk.vm.ci.meta.JavaConstant;
5053
import jdk.vm.ci.meta.JavaKind;
54+
import jdk.vm.ci.meta.ResolvedJavaMethod;
5155

5256
/**
5357
* Provides functionality for scanning constant objects.
@@ -267,7 +271,7 @@ public final void scanConstant(JavaConstant value, ScanReason reason) {
267271
* message if the constant is reachable from multiple places.
268272
*/
269273
public static void unsupportedFeatureDuringConstantScan(BigBang bb, JavaConstant constant, UnsupportedFeatureException e, ScanReason reason) {
270-
unsupportedFeature(bb, String.valueOf(constant.hashCode()), e.getMessage(), reason);
274+
unsupportedFeature(bb, String.valueOf(receiverHashCode(constant)), e.getMessage(), reason);
271275
}
272276

273277
/**
@@ -276,7 +280,27 @@ public static void unsupportedFeatureDuringConstantScan(BigBang bb, JavaConstant
276280
* heap scanning and the heap verification would scan a field that contains an illegal value.
277281
*/
278282
public static void unsupportedFeatureDuringFieldScan(BigBang bb, AnalysisField field, JavaConstant receiver, UnsupportedFeatureException e, ScanReason reason) {
279-
unsupportedFeature(bb, (receiver != null ? receiver.hashCode() + "_" : "") + field.format("%H.%n"), e.getMessage(), reason);
283+
unsupportedFeature(bb, (receiver != null ? receiverHashCode(receiver) + "_" : "") + field.format("%H.%n"), e.getMessage(), reason);
284+
}
285+
286+
public static void unsupportedFeatureDuringFieldFolding(BigBang bb, AnalysisField field, JavaConstant receiver, UnsupportedFeatureException e, AnalysisMethod parsedMethod, int bci) {
287+
ScanReason reason = new FieldConstantFold(field, parsedMethod, bci, receiver, new MethodParsing(parsedMethod));
288+
unsupportedFeature(bb, (receiver != null ? receiverHashCode(receiver) + "_" : "") + field.format("%H.%n"), e.getMessage(), reason);
289+
}
290+
291+
/**
292+
* The {@link ImageHeapScanner} may find issue when scanning the {@link ImageHeapConstant}
293+
* whereas the {@link HeapSnapshotVerifier} may find issues when scanning the original hosted
294+
* objects. Use a consistent hash code as a key to map them to the same error message.
295+
*/
296+
private static int receiverHashCode(JavaConstant receiver) {
297+
if (receiver instanceof ImageHeapConstant) {
298+
JavaConstant hostedObject = ((ImageHeapConstant) receiver).getHostedObject();
299+
if (hostedObject != null) {
300+
return hostedObject.hashCode();
301+
}
302+
}
303+
return receiver.hashCode();
280304
}
281305

282306
public static void unsupportedFeature(BigBang bb, String key, String message, ScanReason reason) {
@@ -311,13 +335,28 @@ public static AnalysisMethod buildObjectBacktrace(BigBang bb, ScanReason reason,
311335
}
312336

313337
static String asString(BigBang bb, ScanReason reason) {
314-
if (reason instanceof FieldScan) {
338+
if (reason instanceof MethodParsing) {
339+
MethodParsing mp = (MethodParsing) reason;
340+
String str = String.format("parsing method %s reachable via the parsing context", mp.getMethod().asStackTraceElement(0));
341+
str += ReportUtils.parsingContext(mp.getMethod(), indent + indent);
342+
return str;
343+
} else if (reason instanceof FieldConstantFold) {
344+
FieldConstantFold fieldFold = (FieldConstantFold) reason;
345+
StackTraceElement location = fieldFold.parsedMethod.asStackTraceElement(fieldFold.bci);
346+
if (fieldFold.field.isStatic()) {
347+
return "trying to constant fold static field " + reason + "\n at " + location;
348+
} else {
349+
/* Instance field scans must have a receiver, hence the 'of'. */
350+
return "trying to constant fold field " + reason + " of constant \n " + asString(bb, reason.constant) + "\n at " + location;
351+
}
352+
} else if (reason instanceof FieldScan) {
315353
FieldScan fieldScan = (FieldScan) reason;
316354
if (fieldScan.field.isStatic()) {
317-
return "reading static field " + reason;
355+
return "reading static field " + reason + "\n at " + fieldScan.location();
318356
} else {
319357
/* Instance field scans must have a receiver, hence the 'of'. */
320358
return "reading field " + reason + " of constant \n " + asString(bb, reason.constant);
359+
// + "\n at " + location;
321360
}
322361
} else if (reason instanceof EmbeddedRootScan) {
323362
return "scanning root " + asString(bb, reason.constant) + " embedded in \n " + reason;
@@ -472,25 +511,93 @@ public String toString() {
472511
public static class FieldScan extends ScanReason {
473512
final AnalysisField field;
474513

514+
private static ScanReason previous(AnalysisField field) {
515+
assert field.isStatic() && field.isRead();
516+
Object readBy = field.getReadBy();
517+
if (readBy instanceof BytecodePosition) {
518+
ResolvedJavaMethod readingMethod = ((BytecodePosition) readBy).getMethod();
519+
return new MethodParsing((AnalysisMethod) readingMethod);
520+
} else if (readBy instanceof AnalysisMethod) {
521+
return new MethodParsing((AnalysisMethod) readBy);
522+
} else {
523+
return new OtherReason("registered as read because: " + readBy);
524+
}
525+
}
526+
475527
public FieldScan(AnalysisField field) {
476-
this(field, null, null);
528+
this(field, null, previous(field));
477529
}
478530

479531
public FieldScan(AnalysisField field, JavaConstant receiver, ScanReason previous) {
480532
super(previous, receiver);
481533
this.field = field;
534+
assert field.isRead();
482535
}
483536

484537
public AnalysisField getField() {
485538
return field;
486539
}
487540

541+
public String location() {
542+
Object readBy = field.getReadBy();
543+
if (readBy instanceof BytecodePosition) {
544+
BytecodePosition position = (BytecodePosition) readBy;
545+
return position.getMethod().asStackTraceElement(position.getBCI()).toString();
546+
} else if (readBy instanceof AnalysisMethod) {
547+
return ((AnalysisMethod) readBy).asStackTraceElement(0).toString();
548+
} else {
549+
return "<unknown-location>";
550+
}
551+
}
552+
553+
@Override
554+
public String toString() {
555+
return field.format("%H.%n");
556+
}
557+
}
558+
559+
public static class FieldConstantFold extends ScanReason {
560+
final AnalysisField field;
561+
private final AnalysisMethod parsedMethod;
562+
private final int bci;
563+
564+
public FieldConstantFold(AnalysisField field, AnalysisMethod parsedMethod, int bci, JavaConstant receiver, ScanReason previous) {
565+
super(previous, receiver);
566+
this.field = field;
567+
this.parsedMethod = parsedMethod;
568+
this.bci = bci;
569+
}
570+
488571
@Override
489572
public String toString() {
490573
return field.format("%H.%n");
491574
}
492575
}
493576

577+
public static class MethodParsing extends ScanReason {
578+
final AnalysisMethod method;
579+
580+
public MethodParsing(AnalysisMethod method) {
581+
this(method, null);
582+
}
583+
584+
public MethodParsing(AnalysisMethod method, ScanReason previous) {
585+
super(previous, null);
586+
this.method = method;
587+
}
588+
589+
public AnalysisMethod getMethod() {
590+
return method;
591+
}
592+
593+
@Override
594+
public String toString() {
595+
String str = String.format("Parsing method %s %n", method.asStackTraceElement(0));
596+
str += "Parsing context:" + ReportUtils.parsingContext(method);
597+
return str;
598+
}
599+
}
600+
494601
public static class ArrayScan extends ScanReason {
495602
final AnalysisType arrayType;
496603

@@ -509,7 +616,7 @@ public static class EmbeddedRootScan extends ScanReason {
509616
final BytecodePosition position;
510617

511618
public EmbeddedRootScan(BytecodePosition nodeSourcePosition, JavaConstant root) {
512-
this(nodeSourcePosition, root, null);
619+
this(nodeSourcePosition, root, new MethodParsing((AnalysisMethod) nodeSourcePosition.getMethod()));
513620
}
514621

515622
public EmbeddedRootScan(BytecodePosition nodeSourcePosition, JavaConstant root, ScanReason previous) {

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/PointsToAnalysis.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,6 @@
8181
import com.oracle.svm.util.ClassUtil;
8282
import com.oracle.svm.util.ImageGeneratorThreadMarker;
8383

84-
import jdk.vm.ci.code.BytecodePosition;
8584
import jdk.vm.ci.meta.JavaKind;
8685
import jdk.vm.ci.meta.JavaType;
8786

@@ -347,13 +346,12 @@ public AnalysisMethod addRootMethod(AnalysisMethod aMethod, boolean invokeSpecia
347346
* the callee is linked and registered as implementation-invoked.
348347
*/
349348
postTask(() -> {
350-
BytecodePosition location = new BytecodePosition(null, pointsToMethod, 0);
351349
if (invokeSpecial) {
352350
pointsToMethod.registerAsDirectRootMethod();
353351
} else {
354352
pointsToMethod.registerAsVirtualRootMethod();
355353
}
356-
InvokeTypeFlow invoke = pointsToMethod.initAndGetContextInsensitiveInvoke(PointsToAnalysis.this, location, invokeSpecial);
354+
InvokeTypeFlow invoke = pointsToMethod.initAndGetContextInsensitiveInvoke(PointsToAnalysis.this, null, invokeSpecial);
357355
/*
358356
* Initialize the type flow of the invoke's actual parameters with the
359357
* corresponding parameter declared type. Thus, when the invoke links callees it

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ReachabilityAnalysis.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,8 @@ default void markFieldAccessed(AnalysisField field) {
109109
field.registerAsAccessed();
110110
}
111111

112-
default void markFieldRead(AnalysisField field) {
113-
field.registerAsRead(null);
112+
default void markFieldRead(AnalysisField field, Object reason) {
113+
field.registerAsRead(reason);
114114
}
115115

116116
default void markFieldWritten(AnalysisField field) {

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/PointstoOptions.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ public class PointstoOptions {
8989
@Option(help = "Track the callers for methods and accessing methods for fields.")//
9090
public static final OptionKey<Boolean> TrackAccessChain = new OptionKey<>(false);
9191

92+
@Option(help = "Limit the parsing context depth. Default value is arbitrary set at 100.")//
93+
public static final OptionKey<Integer> ParsingContextMaxDepth = new OptionKey<>(100);
94+
9295
@Option(help = "Track the input for type flows.")//
9396
public static final OptionKey<Boolean> TrackInputFlows = new OptionKey<>(false);
9497

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/MethodTypeFlow.java

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626

2727
import static jdk.vm.ci.common.JVMCIError.shouldNotReachHere;
2828

29-
import java.util.ArrayList;
3029
import java.util.Arrays;
3130
import java.util.Collection;
3231
import java.util.Collections;
@@ -44,6 +43,8 @@
4443
import com.oracle.graal.pointsto.typestate.TypeState;
4544
import com.oracle.graal.pointsto.util.AnalysisError;
4645

46+
import jdk.vm.ci.code.BytecodePosition;
47+
4748
public class MethodTypeFlow extends TypeFlow<AnalysisMethod> {
4849

4950
protected final PointsToAnalysisMethod method;
@@ -87,6 +88,9 @@ protected void ensureFlowsGraphCreated(PointsToAnalysis bb, InvokeTypeFlow reaso
8788
/* All threads that try to parse the current method synchronize and only the first parses. */
8889
private synchronized void createFlowsGraph(PointsToAnalysis bb, InvokeTypeFlow reason) {
8990
if (flowsGraph == null) {
91+
AnalysisError.guarantee(reason == null || reason.getSource() == null ||
92+
!reason.getSource().getMethod().equals(method), "Parsing reason cannot be in the target method itself " + method.format("%H.%n"));
93+
9094
parsingReason = reason;
9195
try {
9296
MethodTypeFlowBuilder builder = bb.createMethodTypeFlowBuilder(bb, method);
@@ -163,18 +167,8 @@ public int getReturnedParameterIndex() {
163167
return returnedParameterIndex;
164168
}
165169

166-
public StackTraceElement[] getParsingContext() {
167-
List<StackTraceElement> parsingContext = new ArrayList<>();
168-
InvokeTypeFlow invokeFlow = parsingReason;
169-
170-
/* Defend against cycles in the parsing context. GR-35744 should fix this properly. */
171-
int maxSize = 100;
172-
173-
while (invokeFlow != null && parsingContext.size() < maxSize) {
174-
parsingContext.add(invokeFlow.getSource().getMethod().asStackTraceElement(invokeFlow.getSource().getBCI()));
175-
invokeFlow = ((PointsToAnalysisMethod) invokeFlow.getSource().getMethod()).getTypeFlow().parsingReason;
176-
}
177-
return parsingContext.toArray(new StackTraceElement[0]);
170+
public BytecodePosition getParsingReason() {
171+
return parsingReason != null ? parsingReason.getSource() : null;
178172
}
179173

180174
@Override

0 commit comments

Comments
 (0)