/******************************************************************************* * Copyright (c) 2011, 2016 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 * Simon Marchi (Ericsson) - Remove a catch that just fails a test. * Simon Marchi (Ericsson) - Disable tests for gdb < 7.2. *******************************************************************************/ package org.eclipse.cdt.tests.dsf.gdb.tests; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.File; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.eclipse.cdt.debug.core.CDIDebugModel; import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; import org.eclipse.cdt.debug.core.model.ICBreakpointType; import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; import org.eclipse.cdt.dsf.concurrent.ImmediateDataRequestMonitor; import org.eclipse.cdt.dsf.concurrent.Query; import org.eclipse.cdt.dsf.datamodel.DMContexts; import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext; 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.FormattedValueDMData; import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; import org.eclipse.cdt.dsf.debug.service.IRunControl.StepType; import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMData; import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlShutdownDMEvent; import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; import org.eclipse.cdt.dsf.gdb.IGdbDebugConstants; import org.eclipse.cdt.dsf.gdb.launching.InferiorRuntimeProcess; import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl; import org.eclipse.cdt.dsf.mi.service.MIExpressions; import org.eclipse.cdt.dsf.mi.service.command.events.MIStoppedEvent; import org.eclipse.cdt.dsf.mi.service.command.output.CLITraceInfo; import org.eclipse.cdt.dsf.mi.service.command.output.MIBreakListInfo; import org.eclipse.cdt.dsf.mi.service.command.output.MIBreakpoint; import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; import org.eclipse.cdt.dsf.service.DsfServicesTracker; import org.eclipse.cdt.dsf.service.DsfSession; import org.eclipse.cdt.tests.dsf.gdb.framework.BaseParametrizedTestCase; import org.eclipse.cdt.tests.dsf.gdb.framework.Intermittent; import org.eclipse.cdt.tests.dsf.gdb.framework.IntermittentRule; import org.eclipse.cdt.tests.dsf.gdb.framework.ServiceEventWaitor; import org.eclipse.cdt.tests.dsf.gdb.framework.SyncUtil; import org.eclipse.cdt.tests.dsf.gdb.launching.TestsPlugin; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.CoreException; import org.eclipse.debug.core.ILaunchManager; import org.eclipse.debug.core.model.IProcess; import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @RunWith(Parameterized.class) @Intermittent(repetition = 3) public class LaunchConfigurationAndRestartTest extends BaseParametrizedTestCase { public @Rule IntermittentRule intermittentRule = new IntermittentRule(); protected static final String EXEC_NAME = "LaunchConfigurationAndRestartTestApp.exe"; protected static final String SOURCE_NAME = "LaunchConfigurationAndRestartTestApp.cc"; protected static final String[] LINE_TAGS = new String[] { "FIRST_LINE_IN_MAIN", "LAST_LINE_IN_MAIN", }; protected int FIRST_LINE_IN_MAIN; protected int LAST_LINE_IN_MAIN; // The exit code returned by the test program private static final int TEST_EXIT_CODE = 36; protected DsfSession fSession; protected DsfServicesTracker fServicesTracker; protected IExpressions fExpService; protected IGDBControl fGdbControl; // Indicates if a restart operation should be done // This allows us to re-use tests for restarts tests protected boolean fRestart; @Override public void doBeforeTest() throws Exception { removeTeminatedLaunchesBeforeTest(); setLaunchAttributes(); // Can't run the launch right away because each test needs to first set some // parameters. The individual tests will be responsible for starting the launch. // Looks up line tags in source file(s). clearLineTags(); resolveLineTagLocations(SOURCE_NAME, LINE_TAGS); FIRST_LINE_IN_MAIN = getLineForTag("FIRST_LINE_IN_MAIN"); LAST_LINE_IN_MAIN = getLineForTag("LAST_LINE_IN_MAIN"); } @Override protected void setLaunchAttributes() { super.setLaunchAttributes(); // Set the binary setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, EXEC_PATH + EXEC_NAME); } // This method cannot be tagged as @Before, because the launch is not // running yet. We have to call this manually after all the proper // parameters have been set for the launch @Override protected void doLaunch() throws Exception { // perform the launch super.doLaunch(); fSession = getGDBLaunch().getSession(); Runnable runnable = new Runnable() { @Override public void run() { fServicesTracker = new DsfServicesTracker(TestsPlugin.getBundleContext(), fSession.getId()); fExpService = fServicesTracker.getService(IExpressions.class); fGdbControl = fServicesTracker.getService(IGDBControl.class); } }; fSession.getExecutor().submit(runnable).get(); // Restart the program if we are testing such a case if (fRestart) { synchronized (this) { wait(1000); // XXX: horrible hack, what are we waiting for? } fRestart = false; SyncUtil.restart(getGDBLaunch()); } } @Override public void doAfterTest() throws Exception { super.doAfterTest(); if (fServicesTracker != null) fServicesTracker.dispose(); } // ********************************************************************* // Below are the tests for the launch configuration. // ********************************************************************* /** * This test will tell the launch to set the working directory to data/launch/bin/ * and will verify that we can find the file LaunchConfigurationAndRestartTestApp.cpp. * This will confirm that GDB has been properly configured with the working dir. */ @Test public void testSettingWorkingDirectory() throws Throwable { String dir = new File(EXEC_PATH).getAbsolutePath(); setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_WORKING_DIRECTORY, dir); setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, dir + "/" + EXEC_NAME); doLaunch(); Query<MIInfo> query = new Query<MIInfo>() { @Override protected void execute(DataRequestMonitor<MIInfo> rm) { fGdbControl.queueCommand( fGdbControl.getCommandFactory().createMIFileExecFile( fGdbControl.getContext(), EXEC_NAME), rm); } }; try { fExpService.getExecutor().execute(query); query.get(TestsPlugin.massageTimeout(500), TimeUnit.MILLISECONDS); } catch (InterruptedException e) { fail(e.getMessage()); } catch (ExecutionException e) { fail(e.getCause().getMessage()); } catch (TimeoutException e) { fail(e.getMessage()); } } /** * This test will verify that a launch will fail if the gdbinit file * does not exist and is not called ".gdbinit". */ @Test public void testSourceInvalidGdbInit() throws Throwable { setLaunchAttribute(IGDBLaunchConfigurationConstants.ATTR_GDB_INIT, "gdbinitThatDoesNotExist"); try { doLaunch(); } catch (CoreException e) { // Success of the test return; } fail("Launch seems to have succeeded even though the gdbinit file did not exist"); } /** * This test will verify that a launch does not fail if the gdbinit file * is called ".gdbinit" and does not exist */ @Test public void testSourceDefaultGdbInit() throws Throwable { setLaunchAttribute(IGDBLaunchConfigurationConstants.ATTR_GDB_INIT, ".gdbinit"); try { doLaunch(); } catch (CoreException e) { fail("Launch has failed even though the gdbinit file has the default name of .gdbinit"); } } /** * This test will tell the launch to use data/launch/src/launchConfigTestGdbinit * as the gdbinit file. We then verify the that the content was properly read. * launchConfigTestGdbinit will simply set some arguments for the program to read; * the arguments are "1 2 3 4 5 6". * * This test is disabled for gdb.7.1 because gdb inserts an extraneous \n that messes up * the launch sequence (more particularly, the byte length detection): * * 17-interpreter-exec console "p/x (char)-1" * ~"\n" * ~"$1 = 0xff\n" * 17^done */ @Test public void testSourceGdbInit() throws Throwable { assumeGdbVersionAtLeast(ITestConstants.SUFFIX_GDB_7_2); setLaunchAttribute(IGDBLaunchConfigurationConstants.ATTR_GDB_INIT, "data/launch/src/launchConfigTestGdbinit"); doLaunch(); MIStoppedEvent stoppedEvent = getInitialStoppedEvent(); // Check that argc is correct final IExpressionDMContext argcDmc = SyncUtil.createExpression(stoppedEvent.getDMContext(), "argc"); Query<FormattedValueDMData> query = new Query<FormattedValueDMData>() { @Override protected void execute(DataRequestMonitor<FormattedValueDMData> rm) { fExpService.getFormattedExpressionValue( fExpService.getFormattedValueContext(argcDmc, MIExpressions.DETAILS_FORMAT), rm); } }; try { fExpService.getExecutor().execute(query); FormattedValueDMData value = query.get(TestsPlugin.massageTimeout(500), TimeUnit.MILLISECONDS); // Argc should be 7: the program name and the six arguments assertTrue("Expected 7 but got " + value.getFormattedValue(), value.getFormattedValue().trim().equals("7")); } catch (InterruptedException e) { fail(e.getMessage()); } catch (ExecutionException e) { fail(e.getCause().getMessage()); } catch (TimeoutException e) { fail(e.getMessage()); } // Check that argv is also correct. For simplicity we only check the last argument final IExpressionDMContext argvDmc = SyncUtil.createExpression(stoppedEvent.getDMContext(), "argv[argc-1]"); Query<FormattedValueDMData> query2 = new Query<FormattedValueDMData>() { @Override protected void execute(DataRequestMonitor<FormattedValueDMData> rm) { fExpService.getFormattedExpressionValue( fExpService.getFormattedValueContext(argvDmc, MIExpressions.DETAILS_FORMAT), rm); } }; try { fExpService.getExecutor().execute(query2); FormattedValueDMData value = query2.get(TestsPlugin.massageTimeout(500), TimeUnit.MILLISECONDS); assertTrue("Expected \"6\" but got " + value.getFormattedValue(), value.getFormattedValue().trim().endsWith("\"6\"")); } catch (InterruptedException e) { fail(e.getMessage()); } catch (ExecutionException e) { fail(e.getCause().getMessage()); } catch (TimeoutException e) { fail(e.getMessage()); } } /** * Repeat the test testSourceGdbInit, but after a restart. */ @Test public void testSourceGdbInitRestart() throws Throwable { assumeGdbVersionAtLeast(ITestConstants.SUFFIX_GDB_7_2); fRestart = true; testSourceGdbInit(); } /** * This test will tell the launch to clear the environment variables. We will * then check that the variable $HOME cannot be found by the program. */ @Test public void testClearingEnvironment() throws Throwable { setLaunchAttribute(ILaunchManager.ATTR_APPEND_ENVIRONMENT_VARIABLES, false); doLaunch(); SyncUtil.runToLocation("envTest"); MIStoppedEvent stoppedEvent = SyncUtil.step(2, StepType.STEP_OVER); // The program has stored the content of $HOME into a variable called 'home'. // Let's verify this variable is 0x0 which means $HOME does not exist. final IExpressionDMContext exprDmc = SyncUtil.createExpression(stoppedEvent.getDMContext(), "home"); Query<FormattedValueDMData> query = new Query<FormattedValueDMData>() { @Override protected void execute(DataRequestMonitor<FormattedValueDMData> rm) { fExpService.getFormattedExpressionValue( fExpService.getFormattedValueContext(exprDmc, MIExpressions.DETAILS_FORMAT), rm); } }; try { fExpService.getExecutor().execute(query); FormattedValueDMData value = query.get(TestsPlugin.massageTimeout(500), TimeUnit.MILLISECONDS); assertTrue("Expected 0x0 but got " + value.getFormattedValue(), value.getFormattedValue().equals("0x0")); } catch (InterruptedException e) { fail(e.getMessage()); } catch (ExecutionException e) { fail(e.getCause().getMessage()); } catch (TimeoutException e) { fail(e.getMessage()); } } /** * Repeat the test testClearingEnvironment, but after a restart. */ @Test public void testClearingEnvironmentRestart() throws Throwable { fRestart = true; testClearingEnvironment(); } /** * This test will tell the launch to set a new environment variable LAUNCHTEST. * We will then check that this new variable can be read by the program. */ @Test public void testSettingEnvironment() throws Throwable { setLaunchAttribute(ILaunchManager.ATTR_APPEND_ENVIRONMENT_VARIABLES, true); Map<String, String> map = new HashMap<String, String>(1); map.put("LAUNCHTEST", "IS SET"); setLaunchAttribute(ILaunchManager.ATTR_ENVIRONMENT_VARIABLES, map); doLaunch(); SyncUtil.runToLocation("envTest"); MIStoppedEvent stoppedEvent = SyncUtil.step(2, StepType.STEP_OVER); // The program has stored the content of $LAUNCHTEST into a variable called 'launchTest'. // Let's verify this variable is set to "IS SET". final IExpressionDMContext exprDmc = SyncUtil.createExpression(stoppedEvent.getDMContext(), "launchTest"); Query<FormattedValueDMData> query = new Query<FormattedValueDMData>() { @Override protected void execute(DataRequestMonitor<FormattedValueDMData> rm) { fExpService.getFormattedExpressionValue( fExpService.getFormattedValueContext(exprDmc, MIExpressions.DETAILS_FORMAT), rm); } }; try { fExpService.getExecutor().execute(query); FormattedValueDMData value = query.get(TestsPlugin.massageTimeout(500), TimeUnit.MILLISECONDS); assertTrue("Expected a string ending with \"IS SET\" but got " + value.getFormattedValue(), value.getFormattedValue().trim().endsWith("\"IS SET\"")); } catch (InterruptedException e) { fail(e.getMessage()); } catch (ExecutionException e) { fail(e.getCause().getMessage()); } catch (TimeoutException e) { fail(e.getMessage()); } // Check that the normal environment is there by checking that $HOME (which is stored in 'home" exists. final IExpressionDMContext exprDmc2 = SyncUtil.createExpression(stoppedEvent.getDMContext(), "home"); Query<FormattedValueDMData> query2 = new Query<FormattedValueDMData>() { @Override protected void execute(DataRequestMonitor<FormattedValueDMData> rm) { fExpService.getFormattedExpressionValue( fExpService.getFormattedValueContext(exprDmc2, MIExpressions.DETAILS_FORMAT), rm); } }; try { fExpService.getExecutor().execute(query2); FormattedValueDMData value = query2.get(TestsPlugin.massageTimeout(500), TimeUnit.MILLISECONDS); assertFalse("Expected something else than 0x0", value.getFormattedValue().equals("0x0")); } catch (InterruptedException e) { fail(e.getMessage()); } catch (ExecutionException e) { fail(e.getCause().getMessage()); } catch (TimeoutException e) { fail(e.getMessage()); } } /** * Repeat the test testSettingEnvironment, but after a restart. */ @Test public void testSettingEnvironmentRestart() throws Throwable { fRestart = true; testSettingEnvironment(); } /** * This test will tell the launch to clear the environment variables and then * set a new environment variable LAUNCHTEST. We will then check that the variable * $HOME cannot be found by the program and that the new variable LAUNCHTEST can be * read by the program. */ @Test public void testClearingAndSettingEnvironment() throws Throwable { setLaunchAttribute(ILaunchManager.ATTR_APPEND_ENVIRONMENT_VARIABLES, false); Map<String, String> map = new HashMap<String, String>(1); map.put("LAUNCHTEST", "IS SET"); setLaunchAttribute(ILaunchManager.ATTR_ENVIRONMENT_VARIABLES, map); doLaunch(); SyncUtil.runToLocation("envTest"); MIStoppedEvent stoppedEvent = SyncUtil.step(2, StepType.STEP_OVER); // The program has stored the content of $LAUNCHTEST into a variable called 'launchTest'. // Let's verify this variable is set to "IS SET". final IExpressionDMContext exprDmc = SyncUtil.createExpression(stoppedEvent.getDMContext(), "launchTest"); Query<FormattedValueDMData> query = new Query<FormattedValueDMData>() { @Override protected void execute(DataRequestMonitor<FormattedValueDMData> rm) { fExpService.getFormattedExpressionValue( fExpService.getFormattedValueContext(exprDmc, MIExpressions.DETAILS_FORMAT), rm); } }; try { fExpService.getExecutor().execute(query); FormattedValueDMData value = query.get(TestsPlugin.massageTimeout(500), TimeUnit.MILLISECONDS); assertTrue("Expected a string ending with \"IS SET\" but got " + value.getFormattedValue(), value.getFormattedValue().trim().endsWith("\"IS SET\"")); } catch (InterruptedException e) { fail(e.getMessage()); } catch (ExecutionException e) { fail(e.getCause().getMessage()); } catch (TimeoutException e) { fail(e.getMessage()); } // The program has stored the content of $HOME into a variable called 'home'. // Let's verify this variable is 0x0 which means it does not exist. final IExpressionDMContext exprDmc2 = SyncUtil.createExpression(stoppedEvent.getDMContext(), "home"); Query<FormattedValueDMData> query2 = new Query<FormattedValueDMData>() { @Override protected void execute(DataRequestMonitor<FormattedValueDMData> rm) { fExpService.getFormattedExpressionValue( fExpService.getFormattedValueContext(exprDmc2, MIExpressions.DETAILS_FORMAT), rm); } }; try { fExpService.getExecutor().execute(query2); FormattedValueDMData value = query2.get(TestsPlugin.massageTimeout(500), TimeUnit.MILLISECONDS); assertTrue("Expected 0x0 but got " + value.getFormattedValue(), value.getFormattedValue().equals("0x0")); } catch (InterruptedException e) { fail(e.getMessage()); } catch (ExecutionException e) { fail(e.getCause().getMessage()); } catch (TimeoutException e) { fail(e.getMessage()); } } /** * Repeat the test testClearingAndSettingEnvironment, but after a restart. */ @Test public void testClearingAndSettingEnvironmentRestart() throws Throwable { fRestart = true; testClearingAndSettingEnvironment(); } /** * This test will tell the launch to set some arguments for the program. We will * then check that the program has the same arguments. * * NOTE: The main setting arguments tests are in {@link CommandLineArgsTest}, this * test remains here to test interaction of command line arguments are restarting. * See {@link #testSettingArgumentsRestart()} */ @Test public void testSettingArguments() throws Throwable { setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS, "1 2 3\n4 5 6"); doLaunch(); MIStoppedEvent stoppedEvent = getInitialStoppedEvent(); // Check that argc is correct final IExpressionDMContext argcDmc = SyncUtil.createExpression(stoppedEvent.getDMContext(), "argc"); Query<FormattedValueDMData> query = new Query<FormattedValueDMData>() { @Override protected void execute(DataRequestMonitor<FormattedValueDMData> rm) { fExpService.getFormattedExpressionValue( fExpService.getFormattedValueContext(argcDmc, MIExpressions.DETAILS_FORMAT), rm); } }; try { fExpService.getExecutor().execute(query); FormattedValueDMData value = query.get(TestsPlugin.massageTimeout(500), TimeUnit.MILLISECONDS); // Argc should be 7: the program name and the six arguments assertTrue("Expected 7 but got " + value.getFormattedValue(), value.getFormattedValue().trim().equals("7")); } catch (InterruptedException e) { fail(e.getMessage()); } catch (ExecutionException e) { fail(e.getCause().getMessage()); } catch (TimeoutException e) { fail(e.getMessage()); } // Check that argv is also correct. For simplicity we only check the last argument final IExpressionDMContext argvDmc = SyncUtil.createExpression(stoppedEvent.getDMContext(), "argv[argc-1]"); Query<FormattedValueDMData> query2 = new Query<FormattedValueDMData>() { @Override protected void execute(DataRequestMonitor<FormattedValueDMData> rm) { fExpService.getFormattedExpressionValue( fExpService.getFormattedValueContext(argvDmc, MIExpressions.DETAILS_FORMAT), rm); } }; try { fExpService.getExecutor().execute(query2); FormattedValueDMData value = query2.get(TestsPlugin.massageTimeout(500), TimeUnit.MILLISECONDS); assertTrue("Expected \"6\" but got " + value.getFormattedValue(), value.getFormattedValue().trim().endsWith("\"6\"")); } catch (InterruptedException e) { fail(e.getMessage()); } catch (ExecutionException e) { fail(e.getCause().getMessage()); } catch (TimeoutException e) { fail(e.getMessage()); } } /** * Repeat the test testSettingArguments, but after a restart. */ @Test public void testSettingArgumentsRestart() throws Throwable { fRestart = true; testSettingArguments(); } /** * This test will tell the launch to "stop on main" at method main(), which we will verify. */ @Test public void testStopAtMain() throws Throwable { setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN, true); setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN_SYMBOL, "main"); doLaunch(); MIStoppedEvent stoppedEvent = getInitialStoppedEvent(); assertTrue("Expected to stop at main:27 but got " + stoppedEvent.getFrame().getFunction() + ":" + Integer.toString(stoppedEvent.getFrame().getLine()), stoppedEvent.getFrame().getFunction().equals("main") && stoppedEvent.getFrame().getLine() == 27); } /** * Repeat the test testStopAtMain, but after a restart. */ @Test public void testStopAtMainRestart() throws Throwable { fRestart = true; testStopAtMain(); } /** * This test will tell the launch to "stop on main" at method stopAtOther(), * which we will then verify. */ @Test public void testStopAtOther() throws Throwable { setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN, true); setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN_SYMBOL, "stopAtOther"); doLaunch(); MIStoppedEvent stoppedEvent = getInitialStoppedEvent(); assertTrue("Expected to stop at stopAtOther but got " + stoppedEvent.getFrame().getFunction() + ":", stoppedEvent.getFrame().getFunction().equals("stopAtOther")); } /** * Repeat the test testStopAtOther, but after a restart. */ @Test public void testStopAtOtherRestart() throws Throwable { fRestart = true; testStopAtOther(); } /** * This test will set a breakpoint at some place in the program and will tell * the launch to NOT "stop on main". We will verify that the first stop is * at the breakpoint that we set. */ @Ignore @Test public void testNoStopAtMain() throws Throwable { setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN, false); // Set this one as well to make sure it gets ignored setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN_SYMBOL, "main"); // We need to set the breakpoint before the launch is started, but the only way to do that is // to set it in the platorm. Ok, but how do I get an IResource that points to my binary? // The current workspace is the JUnit runtime workspace instead of the workspace containing // the JUnit tests. IFile fakeFile = null; CDIDebugModel.createLineBreakpoint(EXEC_PATH + EXEC_NAME, fakeFile, ICBreakpointType.REGULAR, LAST_LINE_IN_MAIN + 1, true, 0, "", true); //$NON-NLS-1$ doLaunch(); MIStoppedEvent stoppedEvent = getInitialStoppedEvent(); assertTrue("Expected to stop at envTest but got " + stoppedEvent.getFrame().getFunction() + ":", stoppedEvent.getFrame().getFunction().equals("envTest")); } /** * Repeat the test testNoStopAtMain, but after a restart. */ @Ignore @Test public void testNoStopAtMainRestart() throws Throwable { fRestart = true; testNoStopAtMain(); } /** * Test that the exit code is available after the inferior as run to * completion so that the console can use it. */ @Test public void testExitCodeSet() throws Throwable { assumeGdbVersionAtLeast(ITestConstants.SUFFIX_GDB_7_3); doLaunch(); ServiceEventWaitor<ICommandControlShutdownDMEvent> shutdownEventWaitor = new ServiceEventWaitor<ICommandControlShutdownDMEvent>( getGDBLaunch().getSession(), ICommandControlShutdownDMEvent.class); // The target is currently stopped. We resume to get it running // and wait for a shutdown event to say execution has completed SyncUtil.resume(); shutdownEventWaitor.waitForEvent(TestsPlugin.massageTimeout(1000)); IProcess[] launchProcesses = getGDBLaunch().getProcesses();; for (IProcess proc : launchProcesses) { if (proc instanceof InferiorRuntimeProcess) { assertThat(proc.getAttribute(IGdbDebugConstants.INFERIOR_EXITED_ATTR), is(notNullValue())); // Wait for the process to terminate so we can obtain its exit code int count = 0; while (count++ < 100 && !proc.isTerminated()) { try { synchronized (proc) { proc.wait(10); } } catch (InterruptedException ie) { } } int exitValue = proc.getExitValue(); assertThat(exitValue, is(TEST_EXIT_CODE)); return; } } assert false; } /** * This test will confirm that we have turned on "pending breakpoints" * The pending breakpoint setting only affects CLI commands so we have * to test with one. We don't have classes to set breakpoints using CLI, * but we do for tracepoints, which is the same for this test. * * The pending breakpoint feature only works with tracepoints starting * with GDB 7.0. * * We could run this test before 7.0 but we would have to use a breakpoint * set using CLI commands. */ @Test public void testPendingBreakpointSetting() throws Throwable { assumeGdbVersionAtLeast(ITestConstants.SUFFIX_GDB_7_0); doLaunch(); MIStoppedEvent stoppedEvent = getInitialStoppedEvent(); final IBreakpointsTargetDMContext bpTargetDmc = DMContexts.getAncestorOfType(stoppedEvent.getDMContext(), IBreakpointsTargetDMContext.class); Query<MIBreakListInfo> query = new Query<MIBreakListInfo>() { @Override protected void execute(final DataRequestMonitor<MIBreakListInfo> rm) { fGdbControl.queueCommand( fGdbControl.getCommandFactory().createCLITrace(bpTargetDmc, "invalid", ""), new ImmediateDataRequestMonitor<CLITraceInfo>(rm) { @Override protected void handleSuccess() { fGdbControl.queueCommand( fGdbControl.getCommandFactory().createMIBreakList(bpTargetDmc), new ImmediateDataRequestMonitor<MIBreakListInfo>(rm) { @Override protected void handleSuccess() { rm.setData(getData()); rm.done(); } }); } }); } }; try { fExpService.getExecutor().execute(query); MIBreakListInfo value = query.get(TestsPlugin.massageTimeout(500), TimeUnit.MILLISECONDS); MIBreakpoint[] bps = value.getMIBreakpoints(); assertTrue("Expected 1 breakpoint but got " + bps.length, bps.length == 1); assertTrue("Expending a <PENDING> breakpoint but got one at " + bps[0].getAddress(), bps[0].getAddress().equals("<PENDING>")); } catch (InterruptedException e) { fail(e.getMessage()); } catch (ExecutionException e) { fail(e.getCause().getMessage()); } catch (TimeoutException e) { fail(e.getMessage()); } } /** * This test will tell the launch to "stop on main" at method main() with reverse * debugging enabled. We will verify that the launch stops at main() and that * reverse debugging is enabled. * * In this test, the execution crosses getenv() while recording is enabled. gdb 7.0 * and 7.1 have trouble with that. We disable the test for those, and enable it for * 7.2 and upwards. */ @Test public void testStopAtMainWithReverse() throws Throwable { assumeGdbVersionAtLeast(ITestConstants.SUFFIX_GDB_7_2); setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN, true); setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN_SYMBOL, "main"); setLaunchAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_REVERSE, true); doLaunch(); MIStoppedEvent stoppedEvent = getInitialStoppedEvent(); // Make sure we stopped at the first line of main assertTrue("Expected to stop at main:" + FIRST_LINE_IN_MAIN + " but got " + stoppedEvent.getFrame().getFunction() + ":" + Integer.toString(stoppedEvent.getFrame().getLine()), stoppedEvent.getFrame().getFunction().equals("main") && stoppedEvent.getFrame().getLine() == FIRST_LINE_IN_MAIN); // Step a couple of times and check where we are final int NUM_STEPS = 3; stoppedEvent = SyncUtil.step(NUM_STEPS, StepType.STEP_OVER); assertTrue("Expected to stop at main:" + (FIRST_LINE_IN_MAIN+NUM_STEPS) + " but got " + stoppedEvent.getFrame().getFunction() + ":" + Integer.toString(stoppedEvent.getFrame().getLine()), stoppedEvent.getFrame().getFunction().equals("main") && stoppedEvent.getFrame().getLine() == FIRST_LINE_IN_MAIN+NUM_STEPS); // Now step backwards to make sure reverse was enabled final ServiceEventWaitor<MIStoppedEvent> eventWaitor = new ServiceEventWaitor<MIStoppedEvent>( fSession, MIStoppedEvent.class); final int REVERSE_NUM_STEPS = 2; final IExecutionDMContext execDmc = stoppedEvent.getDMContext(); Query<MIInfo> query = new Query<MIInfo>() { @Override protected void execute(DataRequestMonitor<MIInfo> rm) { fGdbControl.queueCommand( fGdbControl.getCommandFactory().createMIExecReverseNext(execDmc, REVERSE_NUM_STEPS), rm); } }; try { fGdbControl.getExecutor().execute(query); query.get(TestsPlugin.massageTimeout(500), TimeUnit.MILLISECONDS); } catch (InterruptedException e) { fail(e.getMessage()); } catch (ExecutionException e) { fail(e.getCause().getMessage()); } catch (TimeoutException e) { fail(e.getMessage()); } stoppedEvent = eventWaitor.waitForEvent(TestsPlugin.massageTimeout(1000)); assertTrue("Expected to stop at main:" + (FIRST_LINE_IN_MAIN+NUM_STEPS-REVERSE_NUM_STEPS) + " but got " + stoppedEvent.getFrame().getFunction() + ":" + Integer.toString(stoppedEvent.getFrame().getLine()), stoppedEvent.getFrame().getFunction().equals("main") && stoppedEvent.getFrame().getLine() == FIRST_LINE_IN_MAIN+NUM_STEPS-REVERSE_NUM_STEPS); } /** * Repeat the test testStopAtMainWithReverse, but after a restart. */ @Test public void testStopAtMainWithReverseRestart() throws Throwable { assumeGdbVersionAtLeast(ITestConstants.SUFFIX_GDB_7_2); fRestart = true; testStopAtMainWithReverse(); } /** * This test will tell the launch to "stop on main" at method stopAtOther(), * with reverse debugging enabled. We will then verify that the launch is properly * stopped at stopAtOther() and that it can go backwards until main() (this will * confirm that reverse debugging was enabled at the very start). * * In this test, the execution crosses getenv() while recording is enabled. gdb 7.0 * and 7.1 have trouble with that. We disable the test for those, and enable it for * 7.2 and upwards. */ @Test public void testStopAtOtherWithReverse() throws Throwable { assumeGdbVersionAtLeast(ITestConstants.SUFFIX_GDB_7_2); setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN, true); setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN_SYMBOL, "stopAtOther"); setLaunchAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_REVERSE, true); doLaunch(); MIStoppedEvent stoppedEvent = getInitialStoppedEvent(); // The initial stopped event is not the last stopped event. // With reverse we have to stop the program, turn on reverse and start it again. // Let's get the frame where we really are stopped right now. final IExecutionDMContext execDmc = stoppedEvent.getDMContext(); IFrameDMData frame = SyncUtil.getFrameData(execDmc, 0); // Make sure we stopped at the first line of main assertTrue("Expected to stop at stopAtOther but got " + frame.getFunction(), frame.getFunction().equals("stopAtOther")); // Now step backwards all the way to the start to make sure reverse was enabled from the very start final ServiceEventWaitor<MIStoppedEvent> eventWaitor = new ServiceEventWaitor<MIStoppedEvent>( fSession, MIStoppedEvent.class); final int REVERSE_NUM_STEPS = 3; Query<MIInfo> query2 = new Query<MIInfo>() { @Override protected void execute(DataRequestMonitor<MIInfo> rm) { fGdbControl.queueCommand( fGdbControl.getCommandFactory().createMIExecReverseNext(execDmc, REVERSE_NUM_STEPS), rm); } }; try { fGdbControl.getExecutor().execute(query2); query2.get(TestsPlugin.massageTimeout(500), TimeUnit.MILLISECONDS); } catch (InterruptedException e) { fail(e.getMessage()); } catch (ExecutionException e) { fail(e.getCause().getMessage()); } catch (TimeoutException e) { fail(e.getMessage()); } stoppedEvent = eventWaitor.waitForEvent(TestsPlugin.massageTimeout(1000)); assertTrue("Expected to stop at main:" + (FIRST_LINE_IN_MAIN) + " but got " + stoppedEvent.getFrame().getFunction() + ":" + Integer.toString(stoppedEvent.getFrame().getLine()), stoppedEvent.getFrame().getFunction().equals("main") && stoppedEvent.getFrame().getLine() == FIRST_LINE_IN_MAIN); } /** * Repeat the test testStopAtOtherWithReverse, but after a restart. */ @Test @Ignore("Fails. Investigate what it needs to wait for.") public void testStopAtOtherWithReverseRestart() throws Throwable { assumeGdbVersionAtLeast(ITestConstants.SUFFIX_GDB_7_2); fRestart = true; testStopAtOtherWithReverse(); } /** * This test will set a breakpoint at the last line of the program and will tell * the launch to NOT "stop on main", with reverse debugging enabled. We will * verify that the first stop is at the last line of the program but that the program * can run backwards until main() (this will confirm that reverse debugging was * enabled at the very start). */ @Test @Ignore("TODO: this is not working because it does not insert the breakpoint propertly") public void testNoStopAtMainWithReverse() throws Throwable { assumeGdbVersionAtLeast(ITestConstants.SUFFIX_GDB_7_2); setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN, false); // Set this one as well to make sure it gets ignored setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN_SYMBOL, "main"); setLaunchAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_REVERSE, true); // MUST SET BREAKPOINT AT LAST LINE BUT BEFORE LAUNCH IS STARTED // MUST SET BREAKPOINT AT LAST LINE BUT BEFORE LAUNCH IS STARTED // MUST SET BREAKPOINT AT LAST LINE BUT BEFORE LAUNCH IS STARTED // see testNoStopAtMain() doLaunch(); MIStoppedEvent stoppedEvent = getInitialStoppedEvent(); // The initial stopped event is not the last stopped event. // With reverse we have to stop the program, turn on reverse and start it again. // Let's get the frame where we really are stopped right now. final IExecutionDMContext execDmc = stoppedEvent.getDMContext(); IFrameDMData frame = SyncUtil.getFrameData(execDmc, 0); // Make sure we stopped at the first line of main assertTrue("Expected to stop at main:" + LAST_LINE_IN_MAIN + " but got " + frame.getFunction() + ":" + Integer.toString(frame.getLine()), frame.getFunction().equals("main") && frame.getLine() == LAST_LINE_IN_MAIN); // Now step backwards all the way to the start to make sure reverse was enabled from the very start final ServiceEventWaitor<MIStoppedEvent> eventWaitor = new ServiceEventWaitor<MIStoppedEvent>( fSession, MIStoppedEvent.class); final int REVERSE_NUM_STEPS = 3; Query<MIInfo> query2 = new Query<MIInfo>() { @Override protected void execute(DataRequestMonitor<MIInfo> rm) { fGdbControl.queueCommand( fGdbControl.getCommandFactory().createMIExecReverseNext(execDmc, REVERSE_NUM_STEPS), rm); } }; try { fGdbControl.getExecutor().execute(query2); query2.get(TestsPlugin.massageTimeout(500), TimeUnit.MILLISECONDS); } catch (InterruptedException e) { fail(e.getMessage()); } catch (ExecutionException e) { fail(e.getCause().getMessage()); } catch (TimeoutException e) { fail(e.getMessage()); } stoppedEvent = eventWaitor.waitForEvent(TestsPlugin.massageTimeout(1000)); assertTrue("Expected to stop at main:" + (FIRST_LINE_IN_MAIN) + " but got " + stoppedEvent.getFrame().getFunction() + ":" + Integer.toString(stoppedEvent.getFrame().getLine()), stoppedEvent.getFrame().getFunction().equals("main") && stoppedEvent.getFrame().getLine() == FIRST_LINE_IN_MAIN); } /** * Repeat the test testNoStopAtMainWithReverse, but after a restart. * TODO: remove ignore when parent test is fixed */ @Test @Ignore public void testNoStopAtMainWithReverseRestart() throws Throwable { assumeGdbVersionAtLeast(ITestConstants.SUFFIX_GDB_7_2); fRestart = true; testNoStopAtMainWithReverse(); } }