/******************************************************************************* * Copyright (c) 2007, 2010 Ericsson and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Ericsson - Initial Implementation *******************************************************************************/ package org.eclipse.cdt.tests.dsf.gdb.tests; 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.assertTrue; import java.math.BigInteger; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Map; import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; import org.eclipse.cdt.dsf.concurrent.RequestMonitor; import org.eclipse.cdt.dsf.datamodel.DMContexts; import org.eclipse.cdt.dsf.datamodel.IDMContext; import org.eclipse.cdt.dsf.debug.service.IBreakpoints; import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointDMContext; import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointDMData; import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsAddedEvent; import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsRemovedEvent; import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext; import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsUpdatedEvent; import org.eclipse.cdt.dsf.debug.service.IExpressions; import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; import org.eclipse.cdt.dsf.debug.service.IFormattedValues; import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMContext; import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMData; import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; import org.eclipse.cdt.dsf.internal.DsfPlugin; import org.eclipse.cdt.dsf.mi.service.MIBreakpointDMData; import org.eclipse.cdt.dsf.mi.service.MIBreakpoints; import org.eclipse.cdt.dsf.mi.service.MIBreakpoints.MIBreakpointDMContext; import org.eclipse.cdt.dsf.mi.service.MIRunControl; import org.eclipse.cdt.dsf.mi.service.command.events.MIBreakpointHitEvent; import org.eclipse.cdt.dsf.mi.service.command.events.MIStoppedEvent; import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; import org.eclipse.cdt.dsf.service.DsfServicesTracker; import org.eclipse.cdt.dsf.service.DsfSession; import org.eclipse.cdt.gdb.eventbkpts.IEventBreakpointConstants; import org.eclipse.cdt.gdb.internal.eventbkpts.GdbCatchpoints; import org.eclipse.cdt.tests.dsf.gdb.framework.AsyncCompletionWaitor; import org.eclipse.cdt.tests.dsf.gdb.framework.BackgroundRunner; import org.eclipse.cdt.tests.dsf.gdb.framework.BaseTestCase; import org.eclipse.cdt.tests.dsf.gdb.framework.SyncUtil; import org.eclipse.cdt.tests.dsf.gdb.launching.TestsPlugin; import org.eclipse.core.runtime.Platform; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; /** * This is the test suite for the catchpoint support in DSF-GDB. * * It is meant to be a regression suite to be executed automatically against the * DSF nightly builds. * * It is also meant to be augmented with a proper test case(s) every time a * feature is added or in the event (unlikely :-) that a bug is found in the * Breakpoint Service. * * Refer to the JUnit4 documentation for an explanation of the annotations. * */ @RunWith(BackgroundRunner.class) public class MICatchpointsTest extends BaseTestCase { private static final String TEST_APPL = "data/launch/bin/CatchpointTestApp.exe"; //$NON-NLS-1$ public static final String SOURCE_FILE = "CatchpointTestApp.cc"; //$NON-NLS-1$ public static final int LINE_NUMBER_SLEEP_CALL = 17; // Asynchronous Completion private final AsyncCompletionWaitor fWait = new AsyncCompletionWaitor(); // Services references private DsfSession fSession; private IBreakpointsTargetDMContext fBreakpointsDmc; private DsfServicesTracker fServicesTracker; private MIRunControl fRunControl; private IBreakpoints fBreakpointService; private IExpressions fExpressionService; // Event Management private static Boolean fEventHandlerLock = true; private enum Events { BP_ADDED, BP_UPDATED, BP_REMOVED, BP_HIT } private final int BP_ADDED = Events.BP_ADDED.ordinal(); private final int BP_UPDATED = Events.BP_UPDATED.ordinal(); private final int BP_REMOVED = Events.BP_REMOVED.ordinal(); private final int BP_HIT = Events.BP_HIT.ordinal(); /** number of times a breakpoint event was received, broken down by event type */ private int[] fBreakpointEvents = new int[Events.values().length]; /** total number of breakpoint events received */ private int totalBreakpointEventsCount() { synchronized (fEventHandlerLock) { int total = 0; for (int count : fBreakpointEvents) { total += count; } return total; } } /** * The gdb breakpoint number associated with the most recent breakpoint event */ private int fBreakpointRef; // NOTE: The back-end can reformat the condition. In order for the // comparison to work, better specify the condition as the back-end // would have it. private final String CONDITION_VAR = "g_i"; private final String CONDITION_NONE = ""; private final String CONDITION_1 = CONDITION_VAR + " == 2"; private final String CONDITION_2 = CONDITION_VAR + " == 4"; private final String CONDITION_NEVER_MET = CONDITION_VAR + " == 10000"; private final String CONDITION_ALWAYS_MET = CONDITION_VAR + " >= 0"; // Error messages private final String UNKNOWN_EXECUTION_CONTEXT = "Unknown execution context"; private final String UNKNOWN_BREAKPOINT = "Unknown breakpoint"; // ======================================================================== // Housekeeping stuff // ======================================================================== @BeforeClass public static void testSuiteInitialization() { // Select the binary to run the tests against setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, TEST_APPL); } @AfterClass public static void testSuiteCleanup() { } @Before public void testCaseInitialization() throws Exception { // Get a reference to the breakpoint service fSession = getGDBLaunch().getSession(); Runnable runnable = new Runnable() { public void run() { fServicesTracker = new DsfServicesTracker(TestsPlugin.getBundleContext(), fSession.getId()); assertNotNull(fServicesTracker); fRunControl = fServicesTracker.getService(MIRunControl.class); assertNotNull(fRunControl); fBreakpointService = fServicesTracker.getService(IBreakpoints.class); assertNotNull(fBreakpointService); fExpressionService = fServicesTracker.getService(IExpressions.class); assertNotNull(fExpressionService); // Register to receive breakpoint events fRunControl.getSession().addServiceEventListener(MICatchpointsTest.this, null); clearEventCounters(); } }; fSession.getExecutor().submit(runnable).get(); IContainerDMContext containerDmc = SyncUtil.getContainerContext(); fBreakpointsDmc = DMContexts.getAncestorOfType(containerDmc, IBreakpointsTargetDMContext.class); assertNotNull(fBreakpointsDmc); } @After public void testCaseCleanup() throws Exception { Runnable runnable = new Runnable() { public void run() { fRunControl.getSession().removeServiceEventListener(MICatchpointsTest.this); } }; fSession.getExecutor().submit(runnable).get(); // Clear the references (not strictly necessary) fBreakpointService = null; fRunControl = null; fServicesTracker.dispose(); fServicesTracker = null; clearEventCounters(); } // ======================================================================== // Event Management Functions // ======================================================================== /* ----------------------------------------------------------------------- * eventDispatched * ------------------------------------------------------------------------ * Processes BreakpointHitEvent. * ------------------------------------------------------------------------ * @param e The BreakpointEvent * ------------------------------------------------------------------------ */ @DsfServiceEventHandler public void eventDispatched(IBreakpointsAddedEvent e) { synchronized (fEventHandlerLock) { fBreakpointEvents[BP_ADDED]++; fBreakpointRef = ((MIBreakpointDMContext) e.getBreakpoints()[0]).getReference(); System.out.println(DsfPlugin.getDebugTime() + " Got bp added event (#" + fBreakpointRef + ")"); fEventHandlerLock.notifyAll(); } } @DsfServiceEventHandler public void eventDispatched(IBreakpointsUpdatedEvent e) { synchronized (fEventHandlerLock) { fBreakpointEvents[BP_UPDATED]++; fBreakpointRef = ((MIBreakpointDMContext) e.getBreakpoints()[0]).getReference(); System.out.println(DsfPlugin.getDebugTime() + " Got bp updated event (#" + fBreakpointRef + ")"); fEventHandlerLock.notifyAll(); } } @DsfServiceEventHandler public void eventDispatched(IBreakpointsRemovedEvent e) { synchronized (fEventHandlerLock) { fBreakpointEvents[BP_REMOVED]++; fBreakpointRef = ((MIBreakpointDMContext) e.getBreakpoints()[0]).getReference(); System.out.println(DsfPlugin.getDebugTime() + " Got bp removed event (#" + fBreakpointRef + ")"); fEventHandlerLock.notifyAll(); } } @DsfServiceEventHandler public void eventDispatched(MIBreakpointHitEvent e) { synchronized (fEventHandlerLock) { fBreakpointEvents[BP_HIT]++; fBreakpointRef = e.getNumber(); System.out.println(DsfPlugin.getDebugTime() + " Got bp hit event (#" + fBreakpointRef + ")"); fEventHandlerLock.notifyAll(); } } // Clears the counters private void clearEventCounters() { synchronized (fEventHandlerLock) { for (int i = 0; i < fBreakpointEvents.length; i++) { fBreakpointEvents[i] = 0; } } } // Get the breakpoint hit count private int getBreakpointEventCount(int event) { int count = 0; synchronized (fEventHandlerLock) { count = fBreakpointEvents[event]; } return count; } /** * Suspends the calling thread until [count] number of breakpoint events * have been received in the current test. NOTE: too simple for real life * but good enough for this test suite * * @param count * the number breakpoint events to wait for * @param timeout * max wait time, in milliseconds */ private void waitForBreakpointEvent(int count, int timeout) throws Exception { long startMs = System.currentTimeMillis(); synchronized (fEventHandlerLock) { // Make sure we don't wait forever, in case an event never // arrives. The test will check if everything was received int receivedCount; while ((receivedCount = totalBreakpointEventsCount()) < count) { try { fEventHandlerLock.wait(30); } catch (InterruptedException ex) { } if (System.currentTimeMillis() - startMs > timeout) { throw new Exception("Timed out waiting for " + count + " breakpoint events to occur. Only " + receivedCount + " occurred."); } } } } /** * Simplified variant that just waits up to two seconds */ private void waitForBreakpointEvent(int count) throws Exception { waitForBreakpointEvent(count, TestsPlugin.massageTimeout(2000)); } // ======================================================================== // Helper Functions // ======================================================================== /* ------------------------------------------------------------------------ * evaluateExpression * ------------------------------------------------------------------------ * Invokes the ExpressionService to evaluate an expression. In theory, * we shouldn't rely on another service to test this one but we need a * way to access a variable from the test application in order verify * that the memory operations (read/write) are working properly. * ------------------------------------------------------------------------ * @param expression Expression to resolve @return Resolved expression * @throws InterruptedException * ------------------------------------------------------------------------ */ private BigInteger evaluateExpression(IDMContext ctx, String expression) throws Throwable { // Get a stack context (temporary - should be an MIcontainerDMC) final IExpressionDMContext expressionDMC = SyncUtil.createExpression(ctx, expression); final FormattedValueDMContext formattedValueDMC = SyncUtil.getFormattedValue(fExpressionService, expressionDMC, IFormattedValues.DECIMAL_FORMAT); // Create the DataRequestMonitor which will store the operation result in the wait object final DataRequestMonitor<FormattedValueDMData> drm = new DataRequestMonitor<FormattedValueDMData>(fSession.getExecutor(), null) { @Override protected void handleCompleted() { if (isSuccess()) { fWait.setReturnInfo(getData()); } fWait.waitFinished(getStatus()); } }; // Evaluate the expression (asynchronously) fWait.waitReset(); fSession.getExecutor().submit(new Runnable() { public void run() { fExpressionService.getFormattedExpressionValue(formattedValueDMC, drm); } }); // Wait for completion fWait.waitUntilDone(TestsPlugin.massageTimeout(5000)); assertTrue(fWait.getMessage(), fWait.isOK()); // Return the string formatted by the back-end String result = ""; Object returnInfo = fWait.getReturnInfo(); if (returnInfo instanceof FormattedValueDMData) result = ((FormattedValueDMData) returnInfo).getFormattedValue(); return new BigInteger(result); } /* ------------------------------------------------------------------------ * getBreakpoints * ------------------------------------------------------------------------ * Retrieves the installed breakpoints list * ------------------------------------------------------------------------ * Typical usage: * IBreakpointDMContext[] breakpoints = getBreakpoints(context); * ------------------------------------------------------------------------ * @param context the execution context * ------------------------------------------------------------------------ */ private IBreakpointDMContext[] getBreakpoints(final IBreakpointsTargetDMContext context) throws InterruptedException { // Clear the completion waiter fWait.waitReset(); // Set the Request Monitor final DataRequestMonitor<IBreakpointDMContext[]> drm = new DataRequestMonitor<IBreakpointDMContext[]>(fBreakpointService.getExecutor(), null) { @Override protected void handleCompleted() { fWait.waitFinished(getStatus()); } }; // Issue the breakpoint request fWait.waitReset(); fBreakpointService.getExecutor().submit(new Runnable() { public void run() { fBreakpointService.getBreakpoints(context, drm); } }); // Wait for completion fWait.waitUntilDone(TestsPlugin.massageTimeout(5000)); assertTrue(fWait.getMessage(), fWait.isOK()); // Return the string formatted by the back-end return drm.getData(); } /* ------------------------------------------------------------------------ * getBreakpoint * ------------------------------------------------------------------------ * Retrieves the installed breakpoint * ------------------------------------------------------------------------ * Typical usage: * IBreakpointDMContext breakpoint = ...; * IBreakpointDMData bp = getBreakpoint(breakpoint); * ------------------------------------------------------------------------ * @param breakpoint the breakpoint to retrieve * ------------------------------------------------------------------------ */ private IBreakpointDMData getBreakpoint(final IBreakpointDMContext breakpoint) throws InterruptedException { // Clear the completion waiter fWait.waitReset(); // Set the Request Monitor final DataRequestMonitor<IBreakpointDMData> drm = new DataRequestMonitor<IBreakpointDMData>(fBreakpointService.getExecutor(), null) { @Override protected void handleCompleted() { fWait.waitFinished(getStatus()); } }; // Issue the breakpoint request fWait.waitReset(); fBreakpointService.getExecutor().submit(new Runnable() { public void run() { fBreakpointService.getBreakpointDMData(breakpoint, drm); } }); // Wait for completion fWait.waitUntilDone(TestsPlugin.massageTimeout(5000)); assertTrue(fWait.getMessage(), fWait.isOK()); // Return the string formatted by the back-end return drm.getData(); } /* ------------------------------------------------------------------------ * insertBreakpoint * ------------------------------------------------------------------------ * Issues an add breakpoint request. * ------------------------------------------------------------------------ * Typical usage: * bp = insertBreakpoint(context, attributes); * assertTrue(fWait.getMessage(), fWait.isOK()); * ------------------------------------------------------------------------ * @param context the execution context * @param attributes the breakpoint attributes * ------------------------------------------------------------------------ */ private IBreakpointDMContext insertBreakpoint(final IBreakpointsTargetDMContext context, final Map<String,Object> attributes) throws InterruptedException { // Clear the completion waiter fWait.waitReset(); // Set the Request Monitor final DataRequestMonitor<IBreakpointDMContext> drm = new DataRequestMonitor<IBreakpointDMContext>(fBreakpointService.getExecutor(), null) { @Override protected void handleCompleted() { fWait.waitFinished(getStatus()); } }; // Issue the remove breakpoint request fBreakpointService.getExecutor().submit(new Runnable() { public void run() { fBreakpointService.insertBreakpoint(context, attributes, drm); } }); // Wait for the result and return the breakpoint id fWait.waitUntilDone(TestsPlugin.massageTimeout(5000)); return drm.getData(); } /* ------------------------------------------------------------------------ * removeBreakpoint * ------------------------------------------------------------------------ * Issues a remove breakpoint request. * ------------------------------------------------------------------------ * Typical usage: * IBreakpointDMContext breakpoint = ...; * removeBreakpoint(context, breakpoint); * assertTrue(fWait.getMessage(), fWait.isOK()); * ------------------------------------------------------------------------ * @param breakpoint the breakpoint to remove * ------------------------------------------------------------------------ */ private void removeBreakpoint(final IBreakpointDMContext breakpoint) throws InterruptedException { // Clear the completion waiter fWait.waitReset(); // Set the Request Monitor final RequestMonitor rm = new RequestMonitor(fBreakpointService.getExecutor(), null) { @Override protected void handleCompleted() { fWait.waitFinished(getStatus()); } }; // Issue the add breakpoint request fBreakpointService.getExecutor().submit(new Runnable() { public void run() { fBreakpointService.removeBreakpoint(breakpoint, rm); } }); // Wait for the result fWait.waitUntilDone(TestsPlugin.massageTimeout(5000)); } /* ------------------------------------------------------------------------ * updateBreakpoint * ------------------------------------------------------------------------ * Issues an update breakpoint request. * ------------------------------------------------------------------------ * Typical usage: * updateBreakpoint(context, breakpoint, properties); * assertTrue(fWait.getMessage(), fWait.isOK()); * ------------------------------------------------------------------------ * @param breakpoint the breakpoint to update * @param delta the delta properties * ------------------------------------------------------------------------ */ private void updateBreakpoint(final IBreakpointDMContext breakpoint, final Map<String, Object> delta) throws InterruptedException { // Clear the completion waiter fWait.waitReset(); // Set the Request Monitor final RequestMonitor rm = new RequestMonitor(fBreakpointService.getExecutor(), null) { @Override protected void handleCompleted() { fWait.waitFinished(getStatus()); } }; // Issue the update breakpoint request fBreakpointService.getExecutor().submit(new Runnable() { public void run() { fBreakpointService.updateBreakpoint(breakpoint, delta, rm); } }); // Wait for the result fWait.waitUntilDone(TestsPlugin.massageTimeout(5000)); } // ======================================================================== // Test Cases // ======================================================================== /////////////////////////////////////////////////////////////////////////// // Add Catchpoint tests /////////////////////////////////////////////////////////////////////////// @Test public void insertCatchpoint_InvalidContext() throws Throwable { // Attempt to create a catchpoint with an invalid execution context (should fail) Map<String, Object> breakpoint = new HashMap<String, Object>(); breakpoint.put(MIBreakpoints.BREAKPOINT_TYPE, MIBreakpoints.CATCHPOINT); breakpoint.put(MIBreakpoints.CATCHPOINT_TYPE, "throw"); insertBreakpoint(null, breakpoint); // Ensure it failed String expected = UNKNOWN_EXECUTION_CONTEXT; assertFalse(fWait.getMessage(), fWait.isOK()); assertTrue("Wrong error message: expected message to contain: '" + expected + "', received '" + fWait.getMessage() + "'", fWait.getMessage().contains(expected)); // Ensure that no breakpoint events were received assertEquals("Unexpected number of breakpoint events", 0, totalBreakpointEventsCount()); } // Long story. There's really no way for the user to set a disabled // catchpoint/breakpoint/tracepoint, so this test is invalid. If a // catchpoint is disabled prior to launching a session, then we simply defer // telling gdb about it until the user enables it. It was done this way // because until recently, gdb did not support indicating the enable state // at creation time, and changing the enable state after creation is // susceptible to race condition problems (a non-stopped thread could hit it // during the small window where it's enabled). At some point, we should // change the implementation to use the new gdb capability to create a // disabled breakpoint. When we do, this test will become relevant. // @Test // public void insertCatchpoint_Disabled() throws Throwable { // // Create a catchpoint // Map<String, Object> breakpoint = new HashMap<String, Object>(); // breakpoint.put(MIBreakpoints.BREAKPOINT_TYPE, MIBreakpoints.CATCHPOINT); // breakpoint.put(MIBreakpoints.CATCHPOINT_TYPE, "throw"); // breakpoint.put(MIBreakpoints.IS_ENABLED, false); // // // Perform the test // IBreakpointDMContext ref = insertBreakpoint(fBreakpointsDmc, breakpoint); // assertTrue(fWait.getMessage(), fWait.isOK()); // // // Ensure that right BreakpointEvents were received // waitForBreakpointEvent(1); // int count = totalBreakpointEventsCount(); // assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " // + count, count == 1); // assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_ADDED event(s), received " // + getBreakpointEventCount(BP_ADDED), getBreakpointEventCount(BP_ADDED) == 1); // clearEventCounters(); // // // Ensure that the breakpoint was correctly installed // MIBreakpointDMData breakpoint1 = (MIBreakpointDMData) getBreakpoint(ref); // assertTrue("BreakpointService problem: breakpoint mismatch (wrong condition)", // breakpoint1.getCondition().equals(NO_CONDITION)); // assertTrue("BreakpointService problem: breakpoint mismatch (wrong ignore count)", // breakpoint1.getIgnoreCount() == 0); // assertTrue("BreakpointService problem: breakpoint mismatch (wrong state)", // !breakpoint1.isEnabled()); // // // Ensure the BreakpointService holds only the right breakpoints // IBreakpointDMContext[] breakpoints = getBreakpoints(fBreakpointsDmc); // assertTrue("BreakpointService problem: expected " + 1 + " breakpoint(s), received " // + breakpoints.length, breakpoints.length == 1); // MIBreakpointDMData breakpoint2 = (MIBreakpointDMData) getBreakpoint(breakpoints[0]); // assertEquals(breakpoint1.getNumber(), breakpoint2.getNumber()); // assertFalse(breakpoint2.isEnabled()); // } @Test public void insertCatchpoint_Simple() throws Throwable { IBreakpointDMContext ref = setCatchpoint("throw", null, null); resumeAndExpectBkptHit(((MIBreakpointDMData)getBreakpoint(ref)).getNumber(), 0); } /** * Set a conditional catchpoint. Ensure that it is set correctly in the * back-end. This doesn't actually run the target to see if the catchpoint * behaves correctly. */ @Test public void insertCatchpoint_Condition() throws Throwable { IBreakpointDMContext ref = setCatchpoint("throw", CONDITION_1, null); resumeAndExpectBkptHit(((MIBreakpointDMData)getBreakpoint(ref)).getNumber(), 2); } /** * Set a catchpoint with an ignore count. Ensure that it is set correctly in * the back-end. This doesn't actually run the target to see if the * catchpoint behaves correctly. */ @Test public void insertCatchpoint_IgnoreCnt() throws Throwable { IBreakpointDMContext ref = setCatchpoint("throw", null, 3); resumeAndExpectBkptHit(((MIBreakpointDMData)getBreakpoint(ref)).getNumber(), 3); } /** * Set two different catchpoints and ensure they are set correctly in the back-end. * This doesn't actually run the target to see if the catchpoints behaves * correctly. */ @Test public void insertCatchpoint_MultipleCatchpoints() throws Throwable { // Set a throw catchpoint IBreakpointDMContext ref = setCatchpoint("throw", null, null); MIBreakpointDMData bkpt1_set = (MIBreakpointDMData) getBreakpoint(ref); // Set a catch catchpoint ref = setCatchpoint("catch", null, null); MIBreakpointDMData bkpt2_set = (MIBreakpointDMData) getBreakpoint(ref); // Ensure the breakpoint service sees what we expect IBreakpointDMContext[] breakpoints = getBreakpoints(fBreakpointsDmc); assertEquals("Breakpoint service reports unexpected number of breakpoints", 2, breakpoints.length); MIBreakpointDMData bkpt1_svc = (MIBreakpointDMData) getBreakpoint(breakpoints[0]); MIBreakpointDMData bkpt2_svc = (MIBreakpointDMData) getBreakpoint(breakpoints[1]); // The breakpoint references are not necessarily retrieved in the order the // breakpoints were initially set... if (bkpt1_svc.getNumber() == bkpt1_set.getNumber()) { assertEquals(bkpt2_svc.getNumber(), bkpt2_set.getNumber()); } else { assertEquals(bkpt1_svc.getNumber(), bkpt2_set.getNumber()); assertEquals(bkpt2_svc.getNumber(), bkpt1_set.getNumber()); } } /** * Set two identical catchpoints and ensure they are set correctly in the * back-end. GDB has no problem with this. This doesn't actually run the * target to see if the catchpoints behaves correctly. */ @Test public void insertCatchpoint_Duplicate() throws Throwable { // Set a throw catchpoint IBreakpointDMContext ref = setCatchpoint("throw", null, null); MIBreakpointDMData bkpt1_set = (MIBreakpointDMData) getBreakpoint(ref); // Tell gdb to set a throw catchpoint AGAIN ref = setCatchpoint("throw", null, null); MIBreakpointDMData bkpt2_set = (MIBreakpointDMData) getBreakpoint(ref); // Ensure the breakpoint service sees what we expect IBreakpointDMContext[] breakpoints = getBreakpoints(fBreakpointsDmc); assertEquals("Breakpoint service reports unexpected number of breakpoints", 2, breakpoints.length); MIBreakpointDMData bkpt1_svc = (MIBreakpointDMData) getBreakpoint(breakpoints[0]); MIBreakpointDMData bkpt2_svc = (MIBreakpointDMData) getBreakpoint(breakpoints[1]); // The breakpoint references are not necessarily retrieved in the order the // breakpoints were initially set... if (bkpt1_svc.getNumber() == bkpt1_set.getNumber()) { assertEquals(bkpt2_svc.getNumber(), bkpt2_set.getNumber()); } else { assertEquals(bkpt1_svc.getNumber(), bkpt2_set.getNumber()); assertEquals(bkpt2_svc.getNumber(), bkpt1_set.getNumber()); } } /** * Set a catchpoint while the target is running and ensure it gets hit. */ @Test public void insertCatchpoint_WhileTargetRunning() throws Throwable { // Interrupting the target on Windows is susceptible to an additional, // unwanted suspension. That means that silently interrupting the target // to set/modify/remove a breakpoint then resuming it can leave the // target in a suspended state. Unfortunately, there is nothing // practical CDT can do to address this issue except wait for the gdb // folks to resolve it. See https://bugs.eclipse.org/bugs/show_bug.cgi?id=304096#c27 if (Platform.getOS().equals(Platform.OS_WIN32)) { return; } // Run the program. It will make a two second sleep() call, during which time... SyncUtil.resume(); // Set a throw catchpoint; don't use the utility method since it assumes // the target is running Map<String, Object> bkptsProps = new HashMap<String, Object>(); bkptsProps.put(MIBreakpoints.BREAKPOINT_TYPE, MIBreakpoints.CATCHPOINT); bkptsProps.put(MIBreakpoints.CATCHPOINT_TYPE, "throw"); insertBreakpoint(fBreakpointsDmc, bkptsProps); assertTrue(fWait.getMessage(), fWait.isOK()); // After the sleep, the test app throws a C++ exception. Wait for the // catchpoint to hit and for the expected number of breakpoint events to // have occurred MIStoppedEvent event = SyncUtil.waitForStop(3000); waitForBreakpointEvent(2); // Ensure that right breakpoint events were received. One indicating the // catchpoint was created, another indicating it was hit waitForBreakpointEvent(1); assertEquals("Unexpected number of breakpoint events", 2, totalBreakpointEventsCount()); assertEquals("Unexpected number of breakpoint-added events", 1, getBreakpointEventCount(BP_ADDED)); assertEquals("Unexpected number of breakpoint-hit events", 1, getBreakpointEventCount(BP_HIT)); clearEventCounters(); assertTrue("Did not stop because of catchpoint, but stopped because of: " + event.getClass().getCanonicalName(), event instanceof MIBreakpointHitEvent); } /** * Set a catchpoint and remove it. This doesn't actually run the target to * see if the removed catchpoint has no effect. */ @Test public void removeCatchpoint_SimpleCase() throws Throwable { // Set a throw catchpoint IBreakpointDMContext ref = setCatchpoint("throw", null, null); // Remove the cachpoint clearEventCounters(); removeBreakpoint(ref); assertTrue(fWait.getMessage(), fWait.isOK()); // Ensure that right breakpoint events were received waitForBreakpointEvent(1); assertEquals("Unexpected number of breakpoint events", 1, totalBreakpointEventsCount()); assertEquals("Unexpected number of breakpoint-added events", 1, getBreakpointEventCount(BP_REMOVED)); clearEventCounters(); // Ensure the breakpoint was effectively removed IBreakpointDMContext[] breakpoints = getBreakpoints(fBreakpointsDmc); assertEquals("Breakpoints service reports unexpected number of breakpoints", 0, breakpoints.length); } /** * Set a catchpoint, remove it, then try to remove it again; that should * fail. Set a second catchpoint, try removing the first one (again), which * should again fail, but this time make sure the second catchpoint is * unaffected. This doesn't actually run the target to see if the * insalled/removed catchpoints behave correctly. */ @Test public void removeCatchpoint_InvalidBreakpoint() throws Throwable { // set a catchpoint IBreakpointDMContext bkptRef1 = setCatchpoint("throw", null, null); // Remove the installed breakpoint clearEventCounters(); removeBreakpoint(bkptRef1); assertTrue(fWait.getMessage(), fWait.isOK()); // Ensure that right breakpoints events were received waitForBreakpointEvent(1); assertEquals("Unexpected number of breakpoint events", 1, totalBreakpointEventsCount()); assertEquals("Unexpected number of breakpoint-added events", 1, getBreakpointEventCount(BP_REMOVED)); clearEventCounters(); // Ensure the breakpoint service sees what we expect IBreakpointDMContext[] breakpoints = getBreakpoints(fBreakpointsDmc); assertEquals("Breakpoints service reports unexpected number of breakpoints", 0, breakpoints.length); // Try removing the catchpoint again; should fail removeBreakpoint(bkptRef1); assertFalse(fWait.getMessage(), fWait.isOK()); String expected = UNKNOWN_BREAKPOINT; assertTrue("Wrong error message: expected '" + expected + "', received '" + fWait.getMessage() + "'", fWait.getMessage().contains(expected)); // Ensure no breakpoint events were received assertEquals("Unexpected number of breakpoint events", 0, totalBreakpointEventsCount()); // Ensure the breakpoint service sees what we expect breakpoints = getBreakpoints(fBreakpointsDmc); assertEquals("Breakpoints service reports unexpected number of breakpoints", 0, breakpoints.length); // Re-install the catchpoint IBreakpointDMContext bkptRef2 = setCatchpoint("throw", null, null); clearEventCounters(); // Try removing the un-installed breakpoint again; should fail removeBreakpoint(bkptRef1); assertFalse(fWait.getMessage(), fWait.isOK()); assertTrue("Wrong error message: expected '" + expected + "', received '" + fWait.getMessage() + "'", fWait.getMessage().contains(expected)); // Ensure no breakpoint events were received assertEquals("Unexpected number of breakpoint events", 0, totalBreakpointEventsCount()); // Ensure that the recently set breakpoint is unaffected breakpoints = getBreakpoints(fBreakpointsDmc); assertEquals("Breakpoints service reports unexpected number of breakpoints", 1, breakpoints.length); MIBreakpointDMData bkpt2_set = (MIBreakpointDMData) getBreakpoint(bkptRef2); MIBreakpointDMData bkpt2_svc = (MIBreakpointDMData) getBreakpoint(breakpoints[0]); assertEquals(bkpt2_set.getNumber(), bkpt2_svc.getNumber()); } /** * Set a series of distinct catchpoints then remove them in a different * order. This doesn't actually run the target to see if the * installed/removed catchpoints behave correctly. */ @Test public void removeCatchpoint_MixedOrder() throws Throwable { final String[] events = new String[] { "throw", "catch", "exec", "fork" }; // Set the catchpoints for (String event : events) { setCatchpoint(event, null, null); } // Get the list of breakpoints IBreakpointDMContext[] breakpoints = getBreakpoints(fBreakpointsDmc); assertEquals("Breakpoint service reports unexpected number of breakpoints", events.length, breakpoints.length); // Remove the catchpoints one at a time but in an order different than how they were added int[] whichOne = { 0, 2, 1, 3 }; int breakpoints_left = 4; for (int i = 0; i < whichOne.length; i++) { clearEventCounters(); // Remove one of the catchpoints IBreakpointDMContext removeThisBreakpoint = breakpoints[whichOne[i]]; removeBreakpoint(removeThisBreakpoint); fWait.waitUntilDone(TestsPlugin.massageTimeout(5000)); assertTrue(fWait.getMessage(), fWait.isOK()); // Ensure that right breakpoint events were received waitForBreakpointEvent(1); assertEquals("Unexpected number of breakpoint events", 1, totalBreakpointEventsCount()); assertEquals("Unexpected number of breakpoint-added events", 1, getBreakpointEventCount(BP_REMOVED)); // Ensure the breakpoint service sees what we expect IBreakpointDMContext[] remaining_breakpoints = getBreakpoints(fBreakpointsDmc); assertEquals("Breakpoints service reports unexpected number of breakpoints", --breakpoints_left, remaining_breakpoints.length); for (int j = 0; j < breakpoints_left; j++) { assertTrue("BreakpointService problem: removed breakpoint still present (" + removeThisBreakpoint + ")", !remaining_breakpoints[j].equals(removeThisBreakpoint)); } } } /** * Set a throw and a catch catchpoint while the target is * stopped, then remove the throw catchpoint while the target is running and * ensure the catch catchpoint is hit. */ @Test public void removeCatchpoint_WhileTargetRunning1() throws Throwable { removeCatchpoint_WhileTargetRunning(true); } /** * Variant that removes the catch catchpoint instead of the throw one. See * {@link #removeCatchpoint_WhileTargetRunning1()} */ @Test public void removeCatchpoint_WhileTargetRunning2() throws Throwable { removeCatchpoint_WhileTargetRunning(false); } /** * See {@link #removeCatchpoint_WhileTargetRunning1()} * @param removeThrow * if true, we remove the throw catchpoint, otherwise the catch * one. */ private void removeCatchpoint_WhileTargetRunning(boolean removeThrow) throws Throwable { // Interrupting the target on Windows is susceptible to an additional, // unwanted suspension. That means that silently interrupting the target // to set/modify/remove a breakpoint then resuming it can leave the // target in a suspended state. Unfortunately, there is nothing // practical CDT can do to address this issue except wait for the gdb // folks to resolve it. See https://bugs.eclipse.org/bugs/show_bug.cgi?id=304096#c27 if (Platform.getOS().equals(Platform.OS_WIN32)) { return; } // Set a line breakpoint at the sleep() call. We need to get the program // past the initial loop that throws and catches C++ exceptions. IBreakpointDMContext refLineBkpt = setLineBreakpoint(LINE_NUMBER_SLEEP_CALL); // Run to the breakpoint resumeAndExpectBkptHit(((MIBreakpointDMData) getBreakpoint(refLineBkpt)).getNumber(), null); // Set the two catchpoints IBreakpointDMContext refThrow = setCatchpoint("throw", null, null); IBreakpointDMContext refCatch = setCatchpoint("catch", null, null); // Run the program. It will make a two second sleep() call, during which time... clearEventCounters(); SyncUtil.resume(); // ...we remove one of the catchpoints removeBreakpoint(removeThrow ? refThrow : refCatch); assertTrue(fWait.getMessage(), fWait.isOK()); // After the sleep, the test app throws a C++ exception and catches it. // The catchpoint we DIDN'T remove should stop the program // Wait for catchpoint to hit and for the expected number of breakpoint // events to have occurred MIStoppedEvent event = SyncUtil.waitForStop(3000); waitForBreakpointEvent(2); assertTrue("stopped event is of an unexpected type: " + event.getClass().getName(), event instanceof MIBreakpointHitEvent); MIBreakpointHitEvent bkptHitEvent = (MIBreakpointHitEvent)event; MIBreakpointDMData bkptNotRemoved = (MIBreakpointDMData) getBreakpoint(removeThrow ? refCatch : refThrow); assertEquals("Target stopped as expected, but the responsible breakpoint was not the expected one", bkptNotRemoved.getNumber(), bkptHitEvent.getNumber()); // If we removed the catch exception, we don't know at this point that // it won't get hit; we're stopped at the throw catchpoint. So resume // the target and make sure it doesn't get hit. if (!removeThrow) { clearEventCounters(); SyncUtil.resume(); Thread.sleep(1000); // give the program a second to run to completion assertEquals("Unexpected number of breakpoint events", 0, totalBreakpointEventsCount()); } } /////////////////////////////////////////////////////////////////////////// // Catchpoint Update tests /////////////////////////////////////////////////////////////////////////// /** * Add a catchpoint with no condition then modify the condition. */ @Test public void updateCatchpoint_AddCondition() throws Throwable { // Set a catchpoint with no condition IBreakpointDMContext ref = setCatchpoint("throw", null, null); // Update the catchpoint to have a condition modifyBkptProperty(ref, MIBreakpoints.CONDITION, CONDITION_1); // Ensure the breakpoint service sees what we expect IBreakpointDMContext[] breakpoints = getBreakpoints(fBreakpointsDmc); assertEquals("Breakpoints service reports unexpected number of breakpoints", 1, breakpoints.length); MIBreakpointDMData bkpt_svc = (MIBreakpointDMData) getBreakpoint(breakpoints[0]); assertEquals("Incorrect breakpoint condition", CONDITION_1, bkpt_svc.getCondition()); resumeAndExpectBkptHit(bkpt_svc.getNumber(), 2); } /** * Add a catchpoint with a condition then remove the condition */ @Test public void updateCatchpoint_RemoveCondition() throws Throwable { // Set a catchpoint with a condition IBreakpointDMContext ref = setCatchpoint("throw", CONDITION_1, null); // Remove the condition modifyBkptProperty(ref, MIBreakpoints.CONDITION, null); // Ensure the breakpoint service sees what we expect IBreakpointDMContext[] breakpoints = getBreakpoints(fBreakpointsDmc); assertEquals("Breakpoints service reports unexpected number of breakpoints", 1, breakpoints.length); MIBreakpointDMData bkpt_svc = (MIBreakpointDMData) getBreakpoint(breakpoints[0]); assertEquals("Incorrect breakpoint condition", CONDITION_NONE, bkpt_svc.getCondition()); resumeAndExpectBkptHit(bkpt_svc.getNumber(), 0); } /** * Add a catchpoint with a condition then modify the condition */ @Test public void updateCatchpoint_ModifyCondition() throws Throwable { // Set the catchpoint with a particular condition IBreakpointDMContext ref = setCatchpoint("throw", CONDITION_1, null); // Modify the catchpoint to have a different condition modifyBkptProperty(ref, MIBreakpoints.CONDITION, CONDITION_2); // Ensure the breakpoint service sees what we expect IBreakpointDMContext[] breakpoints = getBreakpoints(fBreakpointsDmc); assertEquals("Breakpoints service reports unexpected number of breakpoints", 1, breakpoints.length); MIBreakpointDMData bkpt_svc = (MIBreakpointDMData) getBreakpoint(breakpoints[0]); assertEquals("Incorrect breakpoint condition", CONDITION_2, bkpt_svc.getCondition()); resumeAndExpectBkptHit(bkpt_svc.getNumber(), 4); } /** * Set a throw and a catch catchpoint while the target is stopped, with a * condition that will never resolve to true. The program should breeze * through the loop that throws and catches C++ exceptions and then enter a * sleep call. During the sleep call, Then remove the throw catchpoint while * the target is running and ensure the catch catchpoint is hit. * */ @Test public void updateCatchpoint_WhileTargetRunning1() throws Throwable { updateCatchpoint_WhileTargetRunning(true); } /** * Variant that removes the catch catchpoint instead of the throw one. See * {@link #removeCatchpoint_WhileTargetRunning1()} */ @Test public void updateCatchpoint_WhileTargetRunning2() throws Throwable { updateCatchpoint_WhileTargetRunning(false); } /** * Set catch and throw catchpoints with a condition that will never be true, * and also a line breakpoint at the sleep call, then resume the target. The * initial part of the program has a loop that throws and catches C++ * exceptions. We should breeze on past that loop because of the invalid * catchpoint conditions and end up stopped at the line breakpoint. We * resume the target. The program makes a sleep call (two seconds), during * which time we attempt to update the condition of one of the catchpooints * to something that will resolve to true. After the sleep, the program does * one more round of throwing and catching. Ensure that the target stops and * that it's because of the catchpoint we updated. * * @param removeThrow * if true, we update the throw catchpoint, otherwise the catch * one. */ private void updateCatchpoint_WhileTargetRunning(boolean modifyThrow) throws Throwable { // Interrupting the target on Windows is susceptible to an additional, // unwanted suspension. That means that silently interrupting the target // to set/modify/remove a breakpoint then resuming it can leave the // target in a suspended state. Unfortunately, there is nothing // practical CDT can do to address this issue except wait for the gdb // folks to resolve it. See https://bugs.eclipse.org/bugs/show_bug.cgi?id=304096#c27 if (Platform.getOS().equals(Platform.OS_WIN32)) { return; } // Set a line breakpoint at the sleep() call. IBreakpointDMContext refLineBkpt = setLineBreakpoint(LINE_NUMBER_SLEEP_CALL); // Set the two catchpoints IBreakpointDMContext refThrow = setCatchpoint("throw", CONDITION_NEVER_MET, null); IBreakpointDMContext refCatch = setCatchpoint("catch", CONDITION_NEVER_MET, null); // Run the program. The catchpoints should not get hit, but the line // breakpoint should clearEventCounters(); SyncUtil.resume(); waitForBreakpointEvent(1); assertEquals("Unexpected number of breakpoint events", 1, totalBreakpointEventsCount()); assertEquals("Unexpected number of breakpoint-added events", 1, getBreakpointEventCount(BP_HIT)); MIBreakpointDMData lineBkpt = (MIBreakpointDMData) getBreakpoint(refLineBkpt); assertEquals("Target stopped as expected, but the responsible breakpoint was not the expected one", lineBkpt.getNumber(), fBreakpointRef); clearEventCounters(); // Resume the program. It will make a one second sleep() call, during which time... SyncUtil.resume(); // ...we modify one of the catchpoints's condition modifyBkptProperty(modifyThrow ? refThrow : refCatch, MIBreakpoints.CONDITION, CONDITION_ALWAYS_MET); // After the sleep, the test app throws a C++ exception and catches it. // So, the catchpoint whose condition we modified should get hit // Wait for breakpoint to hit and for the expected number of breakpoint events to have occurred MIStoppedEvent event = SyncUtil.waitForStop(3000); waitForBreakpointEvent(2); assertTrue("stopped event is of an unexpected type: " + event.getClass().getName(), event instanceof MIBreakpointHitEvent); MIBreakpointHitEvent bkptHitEvent = (MIBreakpointHitEvent)event; MIBreakpointDMData bkptUpdated = (MIBreakpointDMData) getBreakpoint(modifyThrow ? refThrow : refCatch); assertEquals("Target stopped as expected, but the responsible breakpoint was not the expected one", bkptUpdated.getNumber(), bkptHitEvent.getNumber()); } @Test public void updateCatchpoint_AddCount() throws Throwable { // Set the catchpoint with a particular condition IBreakpointDMContext ref = setCatchpoint("throw", null, null); // Modify the catchpoint to have a different condition modifyBkptProperty(ref, MIBreakpoints.IGNORE_COUNT, 3); // Ensure the breakpoint service sees what we expect IBreakpointDMContext[] breakpoints = getBreakpoints(fBreakpointsDmc); assertEquals("Breakpoints service reports unexpected number of breakpoints", 1, breakpoints.length); MIBreakpointDMData bkpt_svc = (MIBreakpointDMData) getBreakpoint(breakpoints[0]); assertEquals("Incorrect breakpoint condition", 3, bkpt_svc.getIgnoreCount()); // Resume and validate catchpoint hit resumeAndExpectBkptHit(bkpt_svc.getNumber(), 3); } /** * Set a catchpoint with an ignore count, then remove the ignore count. */ @Test public void updateCatchpoint_RemoveCount() throws Throwable { // Set the catchpoint with a particular condition IBreakpointDMContext ref = setCatchpoint("throw", null, 3); // Modify the catchpoint to not have an ignore count modifyBkptProperty(ref, MIBreakpoints.IGNORE_COUNT, null); // Ensure the breakpoint service sees what we expect IBreakpointDMContext[] breakpoints = getBreakpoints(fBreakpointsDmc); assertEquals("Breakpoints service reports unexpected number of breakpoints", 1, breakpoints.length); MIBreakpointDMData bkpt_svc = (MIBreakpointDMData) getBreakpoint(breakpoints[0]); assertEquals("Incorrect breakpoint ignore count", 0, bkpt_svc.getIgnoreCount()); // Resume and validate catchpoint hit resumeAndExpectBkptHit(bkpt_svc.getNumber(), 0); } /** * Set a catchpoint with a particular ignore count and then update the * catchpoint to have a different ignore count */ @Test public void updateCatchpoint_ModifyCount() throws Throwable { // Set the catchpoint with a particular ignore count IBreakpointDMContext ref = setCatchpoint("throw", null, 3); // Modify the catchpoint to have a different ignore count modifyBkptProperty(ref, MIBreakpoints.IGNORE_COUNT, 5); // Ensure the breakpoint service sees what we expect IBreakpointDMContext[] breakpoints = getBreakpoints(fBreakpointsDmc); assertEquals("Breakpoints service reports unexpected number of breakpoints", 1, breakpoints.length); MIBreakpointDMData bkpt_svc = (MIBreakpointDMData) getBreakpoint(breakpoints[0]); assertEquals("Incorrect breakpoint ignore count", 5, bkpt_svc.getIgnoreCount()); // Resume and validate catchpoint hit resumeAndExpectBkptHit(bkpt_svc.getNumber(), 5); } /** * Set two catchpoints. Disable one and ensure it isn't hit. Enable it and * ensure it is hit. */ @Test public void updateCatchpoint_Disable() throws Throwable { // Set the catchpoints IBreakpointDMContext refThrow = setCatchpoint("throw", null, null); IBreakpointDMContext refCatch = setCatchpoint("catch", null, null); // Disable the throw catchpoint modifyBkptProperty(refThrow, MIBreakpoints.IS_ENABLED, false); // Ensure the breakpoint service sees what we expect IBreakpointDMContext[] breakpoints = getBreakpoints(fBreakpointsDmc); assertEquals("Breakpoints service reports unexpected number of breakpoints", 2, breakpoints.length); int throwCatchpointNumber = ((MIBreakpointDMData)getBreakpoint(refThrow)).getNumber(); for (IBreakpointDMContext bkpt : breakpoints) { MIBreakpointDMData bkpt_svc = (MIBreakpointDMData) getBreakpoint(bkpt); assertEquals("Incorrect breakpoint condition", throwCatchpointNumber != bkpt_svc.getNumber(), bkpt_svc.isEnabled()); } // Resume the target. Should miss the throw catchpoint and stop at the catch one int catchCatchpointNumber = ((MIBreakpointDMData)getBreakpoint(refCatch)).getNumber(); resumeAndExpectBkptHit(catchCatchpointNumber, null); // Ee-enable the throw catchpoint modifyBkptProperty(refThrow, MIBreakpoints.IS_ENABLED, true); // Ensure the breakpoint service sees what we expect breakpoints = getBreakpoints(fBreakpointsDmc); assertEquals("Breakpoints service reports unexpected number of breakpoints", 2, breakpoints.length); for (IBreakpointDMContext bkpt : breakpoints) { MIBreakpointDMData bkpt_svc = (MIBreakpointDMData) getBreakpoint(bkpt); assertEquals("Incorrect breakpoint condition", true, bkpt_svc.isEnabled()); } // Resume the target. Should miss the throw catchpoint and stop at the catch one resumeAndExpectBkptHit(throwCatchpointNumber, null); } /** * Test some utiility methods we use to convert between event breakpoint ids * and gdb catchpoint keywords */ @Test public void catchpointConversions() throws Throwable { assertEquals("catch", GdbCatchpoints.eventToGdbCatchpointKeyword(IEventBreakpointConstants.EVENT_TYPE_CATCH)); assertEquals("syscall", GdbCatchpoints.eventToGdbCatchpointKeyword(IEventBreakpointConstants.EVENT_TYPE_SYSCALL)); assertEquals(IEventBreakpointConstants.EVENT_TYPE_CATCH, GdbCatchpoints.gdbCatchpointKeywordToEvent("catch")); assertEquals(IEventBreakpointConstants.EVENT_TYPE_SYSCALL, GdbCatchpoints.gdbCatchpointKeywordToEvent("syscall")); assertNull(GdbCatchpoints.gdbCatchpointKeywordToEvent("signa")); assertNull(GdbCatchpoints.gdbCatchpointKeywordToEvent("signals")); } /** * Set a line breakpoint and validate it was set correctly. * * @param lineNumber * the line where to set the breakpoint * @return the breakpoint context */ private IBreakpointDMContext setLineBreakpoint(int lineNumber) throws Exception { clearEventCounters(); IBreakpointDMContext[] bkptsBefore = getBreakpoints(fBreakpointsDmc); // Set the breakpoint Map<String, Object> breakpoint = new HashMap<String, Object>(); breakpoint.put(MIBreakpoints.BREAKPOINT_TYPE, MIBreakpoints.BREAKPOINT); breakpoint.put(MIBreakpoints.FILE_NAME, SOURCE_FILE); breakpoint.put(MIBreakpoints.LINE_NUMBER, lineNumber); IBreakpointDMContext refLineBkpt = insertBreakpoint(fBreakpointsDmc, breakpoint); assertTrue(fWait.getMessage(), fWait.isOK()); // Ensure that right breakpoint events were received. waitForBreakpointEvent(1); assertEquals("Unexpected number of breakpoint events", 1, totalBreakpointEventsCount()); assertEquals("Unexpected number of breakpoint-added events", 1, getBreakpointEventCount(BP_ADDED)); // Ensure the breakpoint service sees what we expect List<IBreakpointDMContext> bkptsAfter = new LinkedList<IBreakpointDMContext>(Arrays.asList(getBreakpoints(fBreakpointsDmc))); assertEquals("Breakpoints service reports unexpected number of breakpoints", bkptsBefore.length + 1, bkptsAfter.size()); ListIterator<IBreakpointDMContext> iter = bkptsAfter.listIterator(); while (iter.hasNext()) { IBreakpointDMContext bkptAfter = iter.next(); boolean found = false; for (IBreakpointDMContext bkptBefore : bkptsBefore) { if (bkptAfter.equals(bkptBefore)) { assertFalse("shouldn't have been more than one match", found); iter.remove(); found = true; } } } assertEquals("All but the new bkpt should have been removed from bkptsAfter", bkptsAfter.size(), 1); return refLineBkpt; } /** * Set a catchpoint for the given event and validate it was set correctly * * @param event * the event; the gdb keyword for it (e.g., "catch", "throw") * @param condition * an optional condition, or null to indicate not condition * @param ignoreCount * an optional ignore count, or null to indicate no ignore count * @return the breakpoint context */ private IBreakpointDMContext setCatchpoint(String event, String condition, Integer ignoreCount) throws Exception { clearEventCounters(); IBreakpointDMContext[] bkptsBefore = getBreakpoints(fBreakpointsDmc); // set the catchpoint Map<String, Object> bkptsProps = new HashMap<String, Object>(); bkptsProps.put(MIBreakpoints.BREAKPOINT_TYPE, MIBreakpoints.CATCHPOINT); bkptsProps.put(MIBreakpoints.CATCHPOINT_TYPE, event); if (condition != null) { bkptsProps.put(MIBreakpoints.CONDITION, condition); } if (ignoreCount != null) { bkptsProps.put(MIBreakpoints.IGNORE_COUNT, ignoreCount); } IBreakpointDMContext refCatchpoint = insertBreakpoint(fBreakpointsDmc, bkptsProps); assertTrue(fWait.getMessage(), fWait.isOK()); // Ensure that right breakpoint events were received. waitForBreakpointEvent(1); assertEquals("Unexpected number of breakpoint events", 1, totalBreakpointEventsCount()); assertEquals("Unexpected number of breakpoint-added events", 1, getBreakpointEventCount(BP_ADDED)); // Ensure the breakpoint service sees what we expect. Ask the breakpoint // service for the list of breakpoint against and make sure it differs // only by the newly added one List<IBreakpointDMContext> bkptsAfter = new LinkedList<IBreakpointDMContext>(Arrays.asList(getBreakpoints(fBreakpointsDmc))); assertEquals("Breakpoints service reports unexpected number of breakpoints", bkptsBefore.length + 1, bkptsAfter.size()); ListIterator<IBreakpointDMContext> iter = bkptsAfter.listIterator(); while (iter.hasNext()) { IBreakpointDMContext bkptAfter = iter.next(); boolean found = false; for (IBreakpointDMContext bkptBefore : bkptsBefore) { if (bkptAfter.equals(bkptBefore)) { assertFalse("shouldn't have been more than one match", found); iter.remove(); found = true; } } } assertEquals("All but the new bkpt should have been removed from bkptsAfter", bkptsAfter.size(), 1); MIBreakpointDMData bkpt_set = (MIBreakpointDMData) getBreakpoint(refCatchpoint); MIBreakpointDMData bkpt_svc = (MIBreakpointDMData) getBreakpoint(bkptsAfter.get(0)); assertEquals(bkpt_set.getNumber(), bkpt_svc.getNumber()); assertEquals("Incorrect breakpoint condition", condition != null ? condition : CONDITION_NONE, bkpt_svc.getCondition()); assertEquals("Incorrect breakpoint ignore count", ignoreCount != null ? ignoreCount : 0, bkpt_svc.getIgnoreCount()); return refCatchpoint; } /** * Resume the target and expect it to be stopped by the given breakpoint. * Optionally, check that the program's single global int variable has the * given value. * * @param bkptNumber * the GDB breakpoint number * @param expectedVarValue * the expected value of the program variable; can be null to * indicate a check isn't wanted * @return the stoppped event */ private MIStoppedEvent resumeAndExpectBkptHit(int bkptNumber, Integer expectedVarValue) throws Throwable { // Resume the target. The throw catchpoint should get hit. clearEventCounters(); MIStoppedEvent event = SyncUtil.resumeUntilStopped(); // Ensure the right breakpoint events were received waitForBreakpointEvent(1); assertEquals("Unexpected number of breakpoint events", 1, totalBreakpointEventsCount()); assertEquals("Unexpected type of breakpoint event", 1, getBreakpointEventCount(BP_HIT)); // Ensure the target stopped because of the throw catchpoint assertEquals("Target stopped as expected, but the responsible breakpoint was not the expected one", bkptNumber, fBreakpointRef); if (expectedVarValue != null) { IFrameDMContext frameDmc = SyncUtil.getStackFrame(event.getDMContext(), 0); assertEquals("program variable has unexpected value", expectedVarValue.intValue(), evaluateExpression(frameDmc, CONDITION_VAR).intValue()); } return event; } /** * Modify a single property of a single breakpoint and validate that a * breakpoint updated event occurs */ private void modifyBkptProperty(IBreakpointDMContext bkptRef, String property, Object value) throws Throwable { // Modify the catchpoint to not have an ignore count clearEventCounters(); Map<String, Object> bkptProps = new HashMap<String, Object>(); bkptProps.put(property, value); updateBreakpoint(bkptRef, bkptProps); assertTrue(fWait.getMessage(), fWait.isOK()); // Ensure that right breakpoint events were received waitForBreakpointEvent(1); assertEquals("Unexpected number of breakpoint events", 1, totalBreakpointEventsCount()); assertEquals("Unexpected number of breakpoint added events", 1, getBreakpointEventCount(BP_UPDATED)); } }