Skip to content

Commit 96253f7

Browse files
committed
Added support for breakpoints with simple run-time conditions
Signed-off-by: Stefan Marr <git@stefan-marr.de>
1 parent 333a471 commit 96253f7

File tree

2 files changed

+82
-16
lines changed
  • truffle/src

2 files changed

+82
-16
lines changed

truffle/src/com.oracle.truffle.api.debug.test/src/com/oracle/truffle/api/debug/test/BreakpointTest.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ public void testBreakpointCondition() {
179179
expectDone();
180180
assertEquals(2, breakpoint.getHitCount());
181181

182-
breakpoint.setCondition(null); // remove the condition
182+
breakpoint.setCondition((String) null); // remove the condition
183183
startEval(testSource);
184184
expectSuspended((SuspendedEvent event) -> {
185185
assertSame(breakpoint, event.getBreakpoints().iterator().next());
@@ -328,8 +328,8 @@ public void testBreakpointsAtSamePlaceHitCorrectly() {
328328
assertEquals(1, event.getBreakpoints().size());
329329
Breakpoint hit = event.getBreakpoints().get(0);
330330
assertSame(breakpoint1, hit);
331-
breakpoint1.setCondition(null);
332-
breakpoint2.setCondition(null);
331+
breakpoint1.setCondition((String) null);
332+
breakpoint2.setCondition((String) null);
333333
assertEquals(out3, getOutput());
334334
});
335335
expectSuspended((SuspendedEvent event) -> {
@@ -801,7 +801,7 @@ public void testGlobalBreakpoints() throws Throwable {
801801
// O.K.
802802
}
803803
try {
804-
newBP.setCondition(null);
804+
newBP.setCondition((String) null);
805805
Assert.fail();
806806
} catch (IllegalStateException ex) {
807807
// O.K.

truffle/src/com.oracle.truffle.api.debug/src/com/oracle/truffle/api/debug/Breakpoint.java

Lines changed: 78 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,15 @@ public enum Kind {
143143
static final Kind[] VALUES = values();
144144
}
145145

146+
/**
147+
* A simple way to have conditional breakpoints, without language-level expressions.
148+
*
149+
* This is meant to implement complex breakpoints for the debugger.
150+
*/
151+
public interface SimpleCondition {
152+
boolean evaluate();
153+
}
154+
146155
private static final Breakpoint BUILDER_INSTANCE = new Breakpoint();
147156

148157
private final SuspendAnchor suspendAnchor;
@@ -159,10 +168,13 @@ public enum Kind {
159168
private volatile boolean resolved;
160169
private volatile int ignoreCount;
161170
private volatile boolean disposed;
171+
162172
private volatile String condition;
163173
private volatile boolean global;
164174
private volatile GlobalBreakpoint roWrapper;
165175

176+
private volatile SimpleCondition simpleCondition;
177+
166178
/* We use long instead of int in the implementation to avoid not hitting again on overflows. */
167179
private final AtomicLong hitCount = new AtomicLong();
168180
private volatile Assumption conditionUnchanged;
@@ -294,8 +306,18 @@ public boolean isResolved() {
294306
* @since 0.9
295307
*/
296308
public synchronized void setCondition(String expression) {
297-
boolean existsChanged = (this.condition == null) != (expression == null);
309+
boolean existsChanged = conditionIsNotSet() != (expression == null);
310+
298311
this.condition = expression;
312+
this.simpleCondition = null;
313+
invalidateConditionUnchangedAssumption(existsChanged);
314+
}
315+
316+
private boolean conditionIsNotSet() {
317+
return this.condition == null && this.simpleCondition == null;
318+
}
319+
320+
private void invalidateConditionUnchangedAssumption(boolean existsChanged) {
299321
Assumption assumption = conditionUnchanged;
300322
if (assumption != null) {
301323
this.conditionUnchanged = null;
@@ -310,6 +332,13 @@ public synchronized void setCondition(String expression) {
310332
}
311333
}
312334

335+
public synchronized void setCondition(SimpleCondition condition) {
336+
boolean existsChanged = conditionIsNotSet() != (condition == null);
337+
this.simpleCondition = condition;
338+
this.condition = null;
339+
invalidateConditionUnchangedAssumption(existsChanged);
340+
}
341+
313342
/**
314343
* Returns the expression used to create the current breakpoint condition, null if no condition
315344
* set.
@@ -1162,7 +1191,7 @@ private abstract static class AbstractBreakpointNode extends DebuggerNode {
11621191
private final Breakpoint breakpoint;
11631192
protected final BranchProfile breakBranch = BranchProfile.create();
11641193

1165-
@Child private ConditionalBreakNode breakCondition;
1194+
@Child private AbstractConditionalBreakNode breakCondition;
11661195
@CompilationFinal private Assumption conditionExistsUnchanged;
11671196
@CompilationFinal(dimensions = 1) private DebuggerSession[] sessions;
11681197
@CompilationFinal private Assumption sessionsUnchanged;
@@ -1173,8 +1202,13 @@ private abstract static class AbstractBreakpointNode extends DebuggerNode {
11731202
initializeSessions();
11741203
this.conditionExistsUnchanged = breakpoint.getConditionExistsUnchanged();
11751204
if (breakpoint.condition != null) {
1205+
assert breakpoint.simpleCondition == null : "We don't support both conditions being set at the same time.";
11761206
this.breakCondition = new ConditionalBreakNode(context, breakpoint);
11771207
}
1208+
if (breakpoint.simpleCondition != null) {
1209+
assert breakpoint.condition == null : "We don't support both conditions being set at the same time.";
1210+
this.breakCondition = new SimpleConditionalBreakNode(context, breakpoint);
1211+
}
11781212
}
11791213

11801214
private void initializeSessions() {
@@ -1275,7 +1309,7 @@ boolean testCondition(VirtualFrame frame) throws BreakpointConditionFailure {
12751309
}
12761310
if (breakCondition != null) {
12771311
try {
1278-
return breakCondition.executeBreakCondition(frame, sessions);
1312+
return breakCondition.shouldBreak(frame, sessions);
12791313
} catch (Throwable e) {
12801314
CompilerDirectives.transferToInterpreter();
12811315
throw new BreakpointConditionFailure(breakpoint, e);
@@ -1328,24 +1362,56 @@ public Throwable getConditionFailure() {
13281362

13291363
}
13301364

1331-
private static class ConditionalBreakNode extends Node {
1365+
private abstract static class AbstractConditionalBreakNode extends Node {
1366+
1367+
protected final EventContext context;
1368+
protected final Breakpoint breakpoint;
1369+
1370+
@Child protected SetThreadSuspensionEnabledNode suspensionEnabledNode = SetThreadSuspensionEnabledNodeGen.create();
1371+
1372+
@CompilationFinal protected Assumption conditionUnchanged;
1373+
1374+
AbstractConditionalBreakNode(EventContext context, Breakpoint breakpoint) {
1375+
this.context = context;
1376+
this.breakpoint = breakpoint;
1377+
this.conditionUnchanged = breakpoint.getConditionUnchanged();
1378+
}
1379+
1380+
abstract boolean shouldBreak(VirtualFrame frame, DebuggerSession[] sessions);
1381+
}
1382+
1383+
private static class SimpleConditionalBreakNode extends AbstractConditionalBreakNode {
1384+
@CompilationFinal private SimpleCondition condition;
1385+
1386+
SimpleConditionalBreakNode(EventContext context, Breakpoint breakpoint) {
1387+
super(context, breakpoint);
1388+
this.condition = breakpoint.simpleCondition;
1389+
}
1390+
1391+
@Override
1392+
boolean shouldBreak(VirtualFrame frame, DebuggerSession[] sessions) {
1393+
if (!conditionUnchanged.isValid()) {
1394+
CompilerDirectives.transferToInterpreterAndInvalidate();
1395+
condition = breakpoint.simpleCondition;
1396+
}
1397+
1398+
return condition.evaluate();
1399+
}
1400+
}
1401+
1402+
private static class ConditionalBreakNode extends AbstractConditionalBreakNode {
13321403

13331404
private static final Object[] EMPTY_ARRAY = new Object[0];
13341405

1335-
private final EventContext context;
1336-
private final Breakpoint breakpoint;
1337-
@Child private SetThreadSuspensionEnabledNode suspensionEnabledNode = SetThreadSuspensionEnabledNodeGen.create();
13381406
@Child private DirectCallNode conditionCallNode;
13391407
@Child private ExecutableNode conditionSnippet;
1340-
@CompilationFinal private Assumption conditionUnchanged;
13411408

13421409
ConditionalBreakNode(EventContext context, Breakpoint breakpoint) {
1343-
this.context = context;
1344-
this.breakpoint = breakpoint;
1345-
this.conditionUnchanged = breakpoint.getConditionUnchanged();
1410+
super(context, breakpoint);
13461411
}
13471412

1348-
boolean executeBreakCondition(VirtualFrame frame, DebuggerSession[] sessions) {
1413+
@Override
1414+
boolean shouldBreak(VirtualFrame frame, DebuggerSession[] sessions) {
13491415
if ((conditionSnippet == null && conditionCallNode == null) || !conditionUnchanged.isValid()) {
13501416
CompilerDirectives.transferToInterpreterAndInvalidate();
13511417
initializeConditional(frame.materialize());

0 commit comments

Comments
 (0)