/* * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.oracle.truffle.api.debug.test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import java.io.File; import org.junit.Assert; import org.junit.Test; import com.oracle.truffle.api.debug.Breakpoint; import com.oracle.truffle.api.debug.DebuggerSession; import com.oracle.truffle.api.debug.SuspendedEvent; import com.oracle.truffle.api.source.Source; import com.oracle.truffle.api.source.SourceSection; public class BreakpointTest extends AbstractDebugTest { @Test public void testBreakpointDefaults() { Source testSource = testSource("STATEMENT"); Breakpoint breakpoint = Breakpoint.newBuilder(testSource).lineIs(1).build(); assertEquals(0, breakpoint.getHitCount()); assertEquals(0, breakpoint.getIgnoreCount()); assertFalse(breakpoint.isDisposed()); assertTrue(breakpoint.isEnabled()); assertFalse(breakpoint.isResolved()); // Make some state changes breakpoint.setIgnoreCount(9); assertEquals(9, breakpoint.getIgnoreCount()); breakpoint.setEnabled(false); assertFalse(breakpoint.isEnabled()); breakpoint.setCondition("a + b"); breakpoint.dispose(); assertTrue(breakpoint.isDisposed()); assertFalse(breakpoint.isEnabled()); assertFalse(breakpoint.isResolved()); } @Test public void testBreakpointResolve() { Source testSource = testSource("ROOT(\n" + "STATEMENT,\n" + "STATEMENT,\n" + "STATEMENT)"); Breakpoint breakpoint2 = Breakpoint.newBuilder(testSource).lineIs(2).build(); assertFalse(breakpoint2.isResolved()); Breakpoint breakpoint3 = Breakpoint.newBuilder(testSource).lineIs(3).build(); assertFalse(breakpoint3.isResolved()); try (DebuggerSession session = startSession()) { session.install(breakpoint2); assertFalse(breakpoint2.isResolved()); assertFalse(breakpoint3.isResolved()); startEval(testSource); expectSuspended((SuspendedEvent event) -> { assertSame(breakpoint2, event.getBreakpoints().iterator().next()); }); assertTrue(breakpoint2.isResolved()); expectDone(); assertTrue(breakpoint2.isResolved()); assertFalse(breakpoint3.isResolved()); // breakpoint3 should be resolved by just installing it session.install(breakpoint3); assertTrue(breakpoint2.isResolved()); assertTrue(breakpoint3.isResolved()); } } @Test public void testBreakpointCondition() { Source testSource = testSource("ROOT(\n" + "STATEMENT,\n" + "STATEMENT,\n" + "STATEMENT)"); try (DebuggerSession session = startSession()) { Breakpoint breakpoint = session.install(Breakpoint.newBuilder(testSource).lineIs(2).build()); breakpoint.setCondition("CONSTANT(true)"); startEval(testSource); expectSuspended((SuspendedEvent event) -> { assertSame(breakpoint, event.getBreakpoints().iterator().next()); assertNull(event.getBreakpointConditionException(breakpoint)); }); assertEquals(1, breakpoint.getHitCount()); expectDone(); breakpoint.setCondition("CONSTANT(false)"); startEval(testSource); assertEquals(1, breakpoint.getHitCount()); expectDone(); breakpoint.setCondition("CONSTANT("); // error by parse exception startEval(testSource); expectSuspended((SuspendedEvent event) -> { assertSame(breakpoint, event.getBreakpoints().iterator().next()); assertNotNull(event.getBreakpointConditionException(breakpoint)); }); assertEquals(2, breakpoint.getHitCount()); expectDone(); } } @Test public void testBreakURI1() throws Throwable { final Source source = testSource("ROOT(\n" + " STATEMENT,\n" + " STATEMENT,\n" + " STATEMENT,\n" + // break here " STATEMENT,\n" + " STATEMENT\n" + ")\n"); Breakpoint globalBreakpoint = null; try (DebuggerSession session = startSession()) { Breakpoint breakpoint = session.install(Breakpoint.newBuilder(source.getURI()).lineIs(4).build()); globalBreakpoint = breakpoint; startEval(source); expectSuspended((SuspendedEvent event) -> { checkState(event, 4, true, "STATEMENT"); Assert.assertEquals(1, event.getBreakpoints().size()); Assert.assertSame(breakpoint, event.getBreakpoints().get(0)); }); Assert.assertEquals(1, breakpoint.getHitCount()); Assert.assertEquals(true, breakpoint.isEnabled()); expectDone(); } Assert.assertEquals(false, globalBreakpoint.isEnabled()); } @Test public void testBreakURI2() throws Throwable { File testFile = testFile("ROOT(\n" + " DEFINE(foo,\n" + " LOOP(3,\n" + " STATEMENT)\n" + " ),\n" + " CALL(foo)\n" + ")\n"); try (DebuggerSession session = startSession()) { Breakpoint breakpoint = session.install(Breakpoint.newBuilder(testFile.toURI()).lineIs(4).build()); session.suspendNextExecution(); startEval(Source.newBuilder(testFile).build()); for (int i = 0; i < 3; i++) { expectSuspended((SuspendedEvent event) -> { checkState(event, 4, true, "STATEMENT").prepareContinue(); }); } Assert.assertEquals(3, breakpoint.getHitCount()); expectDone(); } } @Test public void testDisableBreakpointsDuringSuspend() throws Throwable { Source source = testSource("ROOT(\n" + " DEFINE(foo,\n" + " LOOP(3,\n" + " STATEMENT)\n" + " ),\n" + " CALL(foo)\n" + ")\n"); try (DebuggerSession session = startSession()) { Breakpoint breakpoint1 = session.install(Breakpoint.newBuilder(source).lineIs(4).build()); Breakpoint breakpoint2 = session.install(Breakpoint.newBuilder(source).lineIs(4).build()); Breakpoint breakpoint3 = session.install(Breakpoint.newBuilder(source).lineIs(4).build()); startEval(source); for (int i = 0; i < 3; i++) { expectSuspended((SuspendedEvent event) -> { checkState(event, 4, true, "STATEMENT").prepareContinue(); }); if (i == 0) { breakpoint3.dispose(); } if (i == 1) { breakpoint1.dispose(); } } Assert.assertEquals(2, breakpoint1.getHitCount()); Assert.assertEquals(3, breakpoint2.getHitCount()); Assert.assertEquals(1, breakpoint3.getHitCount()); expectDone(); } } @Test public void testBreakSource() throws Throwable { final Source source = testSource("ROOT(\n" + " STATEMENT,\n" + " STATEMENT,\n" + " STATEMENT,\n" + // break here " STATEMENT,\n" + " STATEMENT\n" + ")\n"); Breakpoint globalBreakpoint = null; try (DebuggerSession session = startSession()) { Breakpoint breakpoint = session.install(Breakpoint.newBuilder(source).lineIs(4).build()); globalBreakpoint = breakpoint; startEval(source); expectSuspended((SuspendedEvent event) -> { checkState(event, 4, true, "STATEMENT"); Assert.assertEquals(1, event.getBreakpoints().size()); Assert.assertSame(breakpoint, event.getBreakpoints().get(0)); }); Assert.assertEquals(1, breakpoint.getHitCount()); Assert.assertEquals(true, breakpoint.isEnabled()); expectDone(); } Assert.assertEquals(false, globalBreakpoint.isEnabled()); } @Test public void testChangeDuringSuspension() throws Throwable { final Source source = testSource("ROOT(\n" + " DEFINE(foo,\n" + " STATEMENT\n" + " ),\n" + " STATEMENT,\n" + " CALL(foo)\n" + ")\n"); try (DebuggerSession session = startSession()) { session.suspendNextExecution(); startEval(source); expectSuspended((SuspendedEvent event) -> { checkState(event, 5, true, "STATEMENT"); Assert.assertEquals(0, event.getBreakpoints().size()); session.install(Breakpoint.newBuilder(source).lineIs(3).build()); event.prepareContinue(); }); expectSuspended((SuspendedEvent event) -> { checkState(event, 3, true, "STATEMENT"); event.prepareContinue(); }); expectDone(); } } @Test public void testOneShot() throws Throwable { final Source source = testSource("ROOT(\n" + " STATEMENT,\n" + " LOOP(3, STATEMENT),\n" + " STATEMENT\n" + ")\n"); try (DebuggerSession session = startSession()) { Breakpoint breakpoint = session.install(Breakpoint.newBuilder(source).lineIs(3).oneShot().build()); startEval(source); expectSuspended((SuspendedEvent event) -> { checkState(event, 3, true, "STATEMENT"); Assert.assertEquals(1, event.getBreakpoints().size()); Assert.assertSame(breakpoint, event.getBreakpoints().iterator().next()); Assert.assertFalse(breakpoint.isEnabled()); Assert.assertEquals(1, breakpoint.getHitCount()); breakpoint.setEnabled(true); // reenable breakpoint to hit again event.prepareContinue(); }); expectSuspended((SuspendedEvent event) -> { Assert.assertEquals(1, event.getBreakpoints().size()); Assert.assertSame(breakpoint, event.getBreakpoints().iterator().next()); Assert.assertFalse(breakpoint.isEnabled()); Assert.assertEquals(2, breakpoint.getHitCount()); event.prepareContinue(); }); // we don't reenable the breakpoint so we should not hit it again expectDone(); } } @Test public void testBreakSourceSection() throws Throwable { final Source source = testSource("ROOT(STATEMENT, STATEMENT, STATEMENT)\n"); try (DebuggerSession session = startSession()) { SourceSection sourceSection = source.createSection(16, 9); Breakpoint breakpoint = session.install(Breakpoint.newBuilder(sourceSection).build()); startEval(source); expectSuspended((SuspendedEvent event) -> { checkState(event, 1, true, "STATEMENT"); Assert.assertEquals(sourceSection, event.getSourceSection()); assertSame(breakpoint, event.getBreakpoints().iterator().next()); event.prepareContinue(); }); expectDone(); } } @Test public void testDisableDispose() throws Throwable { final Source source = testSource("ROOT(\n" + " STATEMENT,\n" + " STATEMENT,\n" + " STATEMENT,\n" + " STATEMENT,\n" + " STATEMENT,\n" + " STATEMENT,\n" + " STATEMENT,\n" + " STATEMENT,\n" + " STATEMENT,\n" + " STATEMENT,\n" + " STATEMENT,\n" + " STATEMENT\n" + ")\n"); try (DebuggerSession session = startSession()) { // test normal breakpoint should hit Breakpoint breakpoint4 = session.install(Breakpoint.newBuilder(source).lineIs(4).build()); // test disposed breakpoint should not hit Breakpoint breakpoint6 = session.install(Breakpoint.newBuilder(source).lineIs(6).build()); breakpoint6.dispose(); // test disabled breakpoint should not hit Breakpoint breakpoint8 = session.install(Breakpoint.newBuilder(source).lineIs(8).build()); breakpoint8.setEnabled(false); // test re-enabled breakpoint should hit Breakpoint breakpoint10 = session.install(Breakpoint.newBuilder(source).lineIs(10).build()); breakpoint10.setEnabled(false); breakpoint10.setEnabled(true); session.suspendNextExecution(); startEval(source); expectSuspended((SuspendedEvent event) -> { checkState(event, 2, true, "STATEMENT").prepareContinue(); }); expectSuspended((SuspendedEvent event) -> { checkState(event, 4, true, "STATEMENT").prepareContinue(); }); expectSuspended((SuspendedEvent event) -> { checkState(event, 10, true, "STATEMENT").prepareContinue(); }); expectDone(); Assert.assertEquals(1, breakpoint4.getHitCount()); Assert.assertTrue(breakpoint4.isEnabled()); Assert.assertEquals(0, breakpoint6.getHitCount()); Assert.assertFalse(breakpoint6.isEnabled()); Assert.assertEquals(0, breakpoint8.getHitCount()); Assert.assertFalse(breakpoint8.isEnabled()); Assert.assertEquals(1, breakpoint10.getHitCount()); Assert.assertTrue(breakpoint10.isEnabled()); } } @Test public void testInactive() throws Throwable { final Source source = testSource("ROOT(\n" + " STATEMENT,\n" + " STATEMENT,\n" + " STATEMENT,\n" + " STATEMENT,\n" + " STATEMENT\n" + ")\n"); // Breakpoints deactivated after the first suspend - no breakpoints are hit try (DebuggerSession session = startSession()) { Assert.assertTrue(session.isBreakpointsActive()); // normal breakpoint Breakpoint breakpoint3 = session.install(Breakpoint.newBuilder(source).lineIs(3).build()); // disabled breakpoint Breakpoint breakpoint4 = session.install(Breakpoint.newBuilder(source).lineIs(4).build()); breakpoint4.setEnabled(false); // re-enabled breakpoint Breakpoint breakpoint5 = session.install(Breakpoint.newBuilder(source).lineIs(5).build()); breakpoint5.setEnabled(false); breakpoint5.setEnabled(true); session.suspendNextExecution(); startEval(source); expectSuspended((SuspendedEvent event) -> { checkState(event, 2, true, "STATEMENT"); session.setBreakpointsActive(false); }); expectDone(); Assert.assertEquals(0, breakpoint3.getHitCount()); Assert.assertEquals(0, breakpoint4.getHitCount()); Assert.assertEquals(0, breakpoint5.getHitCount()); Assert.assertTrue(breakpoint3.isEnabled()); Assert.assertFalse(breakpoint4.isEnabled()); Assert.assertTrue(breakpoint5.isEnabled()); } // Breakpoints deactivated after the first one is hit - the others are not try (DebuggerSession session = startSession()) { Assert.assertTrue(session.isBreakpointsActive()); // normal breakpoint Breakpoint breakpoint2 = session.install(Breakpoint.newBuilder(source).lineIs(2).build()); // disabled breakpoint Breakpoint breakpoint4 = session.install(Breakpoint.newBuilder(source).lineIs(4).build()); breakpoint4.setEnabled(false); // re-enabled breakpoint Breakpoint breakpoint5 = session.install(Breakpoint.newBuilder(source).lineIs(5).build()); breakpoint5.setEnabled(false); breakpoint5.setEnabled(true); session.suspendNextExecution(); startEval(source); expectSuspended((SuspendedEvent event) -> { checkState(event, 2, true, "STATEMENT"); session.setBreakpointsActive(false); }); expectDone(); Assert.assertEquals(1, breakpoint2.getHitCount()); Assert.assertEquals(0, breakpoint4.getHitCount()); Assert.assertEquals(0, breakpoint5.getHitCount()); Assert.assertTrue(breakpoint2.isEnabled()); Assert.assertFalse(breakpoint4.isEnabled()); Assert.assertTrue(breakpoint5.isEnabled()); } // Breakpoints initially deactivated, they are activated before the last one is hit. try (DebuggerSession session = startSession()) { Assert.assertTrue(session.isBreakpointsActive()); session.setBreakpointsActive(false); Assert.assertFalse(session.isBreakpointsActive()); // normal breakpoint Breakpoint breakpoint2 = session.install(Breakpoint.newBuilder(source).lineIs(2).build()); // disabled breakpoint Breakpoint breakpoint4 = session.install(Breakpoint.newBuilder(source).lineIs(4).build()); breakpoint4.setEnabled(false); // re-enabled breakpoint Breakpoint breakpoint5 = session.install(Breakpoint.newBuilder(source).lineIs(5).build()); breakpoint5.setEnabled(false); breakpoint5.setEnabled(true); session.suspendNextExecution(); startEval(source); expectSuspended((SuspendedEvent event) -> { checkState(event, 2, true, "STATEMENT").prepareStepOver(2); }); expectSuspended((SuspendedEvent event) -> { checkState(event, 4, true, "STATEMENT"); session.setBreakpointsActive(true); }); expectSuspended((SuspendedEvent event) -> { checkState(event, 5, true, "STATEMENT"); }); expectDone(); Assert.assertEquals(0, breakpoint2.getHitCount()); Assert.assertEquals(0, breakpoint4.getHitCount()); Assert.assertEquals(1, breakpoint5.getHitCount()); Assert.assertTrue(breakpoint2.isEnabled()); Assert.assertFalse(breakpoint4.isEnabled()); Assert.assertTrue(breakpoint5.isEnabled()); } } }