Skip to content

Commit ecc62bd

Browse files
committed
[GR-54720] Fix RootNode.isCaptureFrameForTrace.
PullRequest: graal/18037
2 parents 9cab6e7 + 604fe2d commit ecc62bd

File tree

12 files changed

+308
-52
lines changed

12 files changed

+308
-52
lines changed
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
/*
2+
* Copyright (c) 2024, 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+
package jdk.graal.compiler.truffle.test;
26+
27+
import static org.junit.Assert.assertEquals;
28+
import static org.junit.Assert.assertTrue;
29+
30+
import java.util.List;
31+
32+
import org.junit.Test;
33+
34+
import com.oracle.truffle.api.CallTarget;
35+
import com.oracle.truffle.api.TruffleStackTrace;
36+
import com.oracle.truffle.api.TruffleStackTraceElement;
37+
import com.oracle.truffle.api.exception.AbstractTruffleException;
38+
import com.oracle.truffle.api.frame.Frame;
39+
import com.oracle.truffle.api.frame.VirtualFrame;
40+
import com.oracle.truffle.api.nodes.Node;
41+
import com.oracle.truffle.api.nodes.RootNode;
42+
import com.oracle.truffle.runtime.OptimizedCallTarget;
43+
44+
public class RootNodeCompilationTest extends TestWithSynchronousCompiling {
45+
46+
@Test
47+
public void testLazyStack() {
48+
var throw0 = new ThrowErrorRootNode();
49+
var root0 = new ConstantTargetRootNode(throw0);
50+
var root1 = new ConstantTargetRootNode(root0);
51+
52+
OptimizedCallTarget target = (OptimizedCallTarget) root1.getCallTarget();
53+
assertFails(() -> target.call(), TestException.class, (e) -> {
54+
for (TruffleStackTraceElement stack : TruffleStackTrace.getStackTrace(e)) {
55+
BaseRootNode root = (BaseRootNode) stack.getTarget().getRootNode();
56+
assertEquals(0b11, stack.getBytecodeIndex());
57+
assertEquals(0, root.compiledCount);
58+
assertEquals(1, root.interpretedCount);
59+
60+
}
61+
});
62+
target.compile(true);
63+
assertFails(() -> target.call(), TestException.class, (e) -> {
64+
for (TruffleStackTraceElement stack : TruffleStackTrace.getStackTrace(e)) {
65+
BaseRootNode root = (BaseRootNode) stack.getTarget().getRootNode();
66+
assertEquals(0b01, stack.getBytecodeIndex());
67+
assertEquals(1, root.compiledCount);
68+
assertEquals(1, root.interpretedCount);
69+
}
70+
});
71+
assertTrue(target.isValid());
72+
}
73+
74+
@Test
75+
@SuppressWarnings("unchecked")
76+
public void testCaptureStack() {
77+
var throw0 = new CaptureStackRootNode();
78+
var root0 = new ConstantTargetRootNode(throw0);
79+
var root1 = new ConstantTargetRootNode(root0);
80+
81+
OptimizedCallTarget target = (OptimizedCallTarget) root1.getCallTarget();
82+
83+
List<TruffleStackTraceElement> trace = (List<TruffleStackTraceElement>) target.call();
84+
for (TruffleStackTraceElement stack : trace) {
85+
BaseRootNode root = (BaseRootNode) stack.getTarget().getRootNode();
86+
assertEquals(0b11, stack.getBytecodeIndex());
87+
assertEquals(0, root.compiledCount);
88+
assertEquals(1, root.interpretedCount);
89+
}
90+
91+
target.compile(true);
92+
93+
trace = (List<TruffleStackTraceElement>) target.call();
94+
for (TruffleStackTraceElement stack : trace) {
95+
BaseRootNode root = (BaseRootNode) stack.getTarget().getRootNode();
96+
assertEquals(0b01, stack.getBytecodeIndex());
97+
assertEquals(1, root.compiledCount);
98+
assertEquals(1, root.interpretedCount);
99+
}
100+
101+
assertTrue(target.isValid());
102+
}
103+
104+
abstract static class BaseRootNode extends RootNode {
105+
106+
int compiledCount;
107+
int interpretedCount;
108+
109+
protected BaseRootNode() {
110+
super(null);
111+
}
112+
113+
@Override
114+
protected boolean isCaptureFramesForTrace(boolean compiledFrame) {
115+
if (compiledFrame) {
116+
compiledCount++;
117+
} else {
118+
interpretedCount++;
119+
}
120+
return !compiledFrame;
121+
}
122+
123+
@Override
124+
protected int findBytecodeIndex(Node node, Frame frame) {
125+
int index = 0;
126+
if (node != null) {
127+
index |= 0b1;
128+
}
129+
if (frame != null) {
130+
index |= 0b10;
131+
}
132+
return index;
133+
}
134+
}
135+
136+
static class ConstantTargetRootNode extends BaseRootNode {
137+
138+
// deliberately
139+
final CallTarget target;
140+
141+
protected ConstantTargetRootNode(RootNode target) {
142+
this.target = target.getCallTarget();
143+
}
144+
145+
@Override
146+
public Object execute(VirtualFrame frame) {
147+
if (target != null) {
148+
return target.call(this);
149+
}
150+
return null;
151+
}
152+
153+
}
154+
155+
static class ThrowErrorRootNode extends BaseRootNode {
156+
157+
@Override
158+
public Object execute(VirtualFrame frame) {
159+
throw new TestException(this);
160+
}
161+
162+
}
163+
164+
static class CaptureStackRootNode extends BaseRootNode {
165+
166+
@Override
167+
public Object execute(VirtualFrame frame) {
168+
return TruffleStackTrace.getStackTrace(new TestException(this));
169+
}
170+
171+
}
172+
173+
@SuppressWarnings("serial")
174+
static class TestException extends AbstractTruffleException {
175+
176+
TestException(Node location) {
177+
super(location);
178+
}
179+
180+
}
181+
182+
}

compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/truffle/test/TestWithPolyglotOptions.java

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@
2424
*/
2525
package jdk.graal.compiler.truffle.test;
2626

27+
import static org.junit.Assert.assertEquals;
28+
import static org.junit.Assert.fail;
29+
30+
import java.util.concurrent.Callable;
31+
import java.util.function.Consumer;
32+
2733
import org.graalvm.polyglot.Context;
2834
import org.graalvm.polyglot.Context.Builder;
2935
import org.junit.After;
@@ -70,4 +76,58 @@ protected final Context setupContext(Context.Builder builder) {
7076
activeContext = newContext;
7177
return newContext;
7278
}
79+
80+
// also known as assertThrows
81+
public static void assertFails(Runnable callable, Class<?> exceptionType) {
82+
assertFails((Callable<?>) () -> {
83+
callable.run();
84+
return null;
85+
}, exceptionType);
86+
}
87+
88+
public static void assertFails(Callable<?> callable, Class<?> exceptionType) {
89+
try {
90+
callable.call();
91+
} catch (Throwable t) {
92+
if (!exceptionType.isInstance(t)) {
93+
throw new AssertionError("expected instanceof " + exceptionType.getName() + " was " + t.toString(), t);
94+
}
95+
return;
96+
}
97+
fail("expected " + exceptionType.getName() + " but no exception was thrown");
98+
}
99+
100+
public static <T extends Throwable> void assertFails(Runnable callable, Class<T> exceptionType, String message) {
101+
assertFails((Callable<?>) () -> {
102+
callable.run();
103+
return null;
104+
}, exceptionType, e -> assertEquals(message, e.getMessage()));
105+
}
106+
107+
public static <T extends Throwable> void assertFails(Runnable run, Class<T> exceptionType, Consumer<T> verifier) {
108+
try {
109+
run.run();
110+
} catch (Throwable t) {
111+
if (!exceptionType.isInstance(t)) {
112+
throw new AssertionError("expected instanceof " + exceptionType.getName() + " was " + t.toString(), t);
113+
}
114+
verifier.accept(exceptionType.cast(t));
115+
return;
116+
}
117+
fail("expected " + exceptionType.getName() + " but no exception was thrown");
118+
}
119+
120+
public static <T extends Throwable> void assertFails(Callable<?> callable, Class<T> exceptionType, Consumer<T> verifier) {
121+
try {
122+
callable.call();
123+
} catch (Throwable t) {
124+
if (!exceptionType.isInstance(t)) {
125+
throw new AssertionError("expected instanceof " + exceptionType.getName() + " was " + t.getClass().getName(), t);
126+
}
127+
verifier.accept(exceptionType.cast(t));
128+
return;
129+
}
130+
fail("expected " + exceptionType.getName() + " but no exception was thrown");
131+
}
132+
73133
}

compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/truffle/test/nodes/RootTestNode.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,13 @@
2424
*/
2525
package jdk.graal.compiler.truffle.test.nodes;
2626

27-
import jdk.graal.compiler.api.directives.GraalDirectives;
28-
2927
import com.oracle.truffle.api.frame.FrameDescriptor;
3028
import com.oracle.truffle.api.frame.VirtualFrame;
31-
import com.oracle.truffle.api.nodes.Node;
3229
import com.oracle.truffle.api.nodes.NodeInfo;
3330
import com.oracle.truffle.api.nodes.RootNode;
3431

32+
import jdk.graal.compiler.api.directives.GraalDirectives;
33+
3534
@NodeInfo
3635
public class RootTestNode extends RootNode {
3736

@@ -87,7 +86,7 @@ public boolean isInternal() {
8786
}
8887

8988
@Override
90-
public boolean isCaptureFramesForTrace(@SuppressWarnings("hiding") Node node) {
89+
public boolean isCaptureFramesForTrace(boolean compiled) {
9190
return captureFramesForTrace;
9291
}
9392
}

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/EspressoRootNode.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ protected int findBytecodeIndex(Node node, Frame frame) {
147147
}
148148

149149
@Override
150-
protected boolean isCaptureFramesForTrace(Node currentNode) {
150+
protected boolean isCaptureFramesForTrace(boolean compiled) {
151151
return true;
152152
}
153153

truffle/CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ This changelog summarizes major changes between Truffle versions relevant to lan
1818
* GR-28103 Deprecated `com.oracle.truffle.api.utilities.JSONHelper` as it is untested, and in its current state does not contain any special logic for dumping AST. JSON printing of AST nodes should be delegated to an external library if necessary. This class will be removed in a future version.
1919
* GR-54085 Added [`MathUtils`](https://www.graalvm.org/truffle/javadoc/com/oracle/truffle/api/utilities/MathUtils.html) API providing additional mathematical functions useful for language implementations, namely: `asinh`, `acosh`, and `atanh`.
2020
* GR-49484 Added `TruffleStackFrameElement.getBytecodeIndex()` to access bytecode indices of a stack frame. Bytecode based languages should consider implementing `RootNode#findBytecodeIndex(Node, Frame)` to resolve the bytecode index.
21-
* GR-49484 Deprecated `RootNode.isCaptureFramesForTrace()`. Implementers should use `RootNode.isCaptureFramesForTrace(Node)` instead.
21+
* GR-49484 Deprecated `RootNode.isCaptureFramesForTrace()`. Implementers should use `RootNode.isCaptureFramesForTrace(boolean)` instead.
2222
* GR-28866 Added `TruffleLanguage.Env.getScopePublic(LanguageInfo)` and `TruffleLanguage.Env.getScopeInternal(LanguageInfo)` to allow languages direct access to other language scopes to implement new polyglot builtins.
2323
* GR-28866 Deprecated `TruffleLanguage.Env.isPolyglotEvalAllowed()`. Replace usages with `TruffleLanguage.Env.isPolyglotEvalAllowed(LanguageInfo)`. Please see javadoc for the updated usage.
2424
* GR-52843 Deprecated `Node.getCost()` and the associated `NodeCost` class without replacement. Truffle DSL no longer generates implementations of this method automatically and will therefore always return `NodeCost.MONOMORPHIC` by default. This is intended to reduce the binary footprint.

truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/RootNodeTest.java

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
import org.junit.Test;
5656

5757
import com.oracle.truffle.api.CallTarget;
58+
import com.oracle.truffle.api.CompilerDirectives;
5859
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
5960
import com.oracle.truffle.api.Truffle;
6061
import com.oracle.truffle.api.TruffleStackTrace;
@@ -229,7 +230,7 @@ static final class TestStackTraceRootNode extends RootNode {
229230
}
230231

231232
@Override
232-
public boolean isCaptureFramesForTrace(Node node) {
233+
public boolean isCaptureFramesForTrace(boolean compiled) {
233234
return this.shouldCaptureFrames;
234235
}
235236

@@ -511,7 +512,7 @@ static FrameDescriptor createFrameDescriptor() {
511512
}
512513

513514
@Override
514-
public boolean isCaptureFramesForTrace(Node node) {
515+
public boolean isCaptureFramesForTrace(boolean compiled) {
515516
return true;
516517
}
517518

@@ -556,7 +557,6 @@ protected int findBytecodeIndex(Node node, Frame frame) {
556557
@SuppressWarnings("unchecked")
557558
public void testHybridBytecodeIndex() {
558559
var result = (List<TruffleStackTraceElement>) new FrameHybridBytecodeIndexRootNode().getCallTarget().call();
559-
560560
assertEquals(1, result.size());
561561
assertEquals(42, result.get(0).getBytecodeIndex());
562562
assertTrue(result.get(0).hasBytecodeIndex());
@@ -575,43 +575,45 @@ static FrameDescriptor createFrameDescriptor() {
575575
return builder.build();
576576
}
577577

578-
@Override
579-
public boolean isCaptureFramesForTrace(Node node) {
580-
return !(node instanceof NodeWithBytecode);
581-
}
582-
583578
@Override
584579
public Object execute(VirtualFrame frame) {
585-
return boundary(frame.materialize());
580+
return boundary(frame.materialize(), CompilerDirectives.inCompiledCode());
586581
}
587582

588583
@TruffleBoundary
589-
private static List<TruffleStackTraceElement> boundary(MaterializedFrame frame) {
590-
frame.setInt(0, 42);
591-
var stackTrace = TruffleStackTrace.getStackTrace(new TestException(frame, null));
592-
assertTrue(Truffle.getRuntime().iterateFrames(new FrameInstanceVisitor<Boolean>() {
593-
@Override
594-
public Boolean visitFrame(FrameInstance frameInstance) {
595-
assertEquals(42, frameInstance.getBytecodeIndex());
596-
return true;
597-
}
598-
}));
599-
600-
frame.setInt(0, 43);
584+
private static List<TruffleStackTraceElement> boundary(MaterializedFrame frame, boolean compiled) {
585+
Node node;
586+
if (compiled) {
587+
frame.setInt(0, 43);
588+
node = new NodeWithBytecode(42);
589+
} else {
590+
frame.setInt(0, 42);
591+
node = null;
592+
}
601593

594+
var stackTrace = TruffleStackTrace.getStackTrace(new TestException(frame, node));
602595
assertTrue(Truffle.getRuntime().iterateFrames(new FrameInstanceVisitor<Boolean>() {
603596
@Override
604597
public Boolean visitFrame(FrameInstance frameInstance) {
605-
assertEquals(43, frameInstance.getBytecodeIndex());
598+
if (frameInstance.getCompilationTier() == 0) {
599+
assertEquals(42, frameInstance.getBytecodeIndex());
600+
} else {
601+
assertEquals(-1, frameInstance.getBytecodeIndex());
602+
}
606603
return true;
607604
}
608605
}));
609606
return stackTrace;
610607
}
611608

609+
@Override
610+
public boolean isCaptureFramesForTrace(boolean compiled) {
611+
return !compiled;
612+
}
613+
612614
@Override
613615
protected int findBytecodeIndex(Node node, Frame frame) {
614-
if (node == null) {
616+
if (frame != null) {
615617
return frame.getInt(0);
616618
} else if (node instanceof NodeWithBytecode n) {
617619
assertNull(frame);

0 commit comments

Comments
 (0)