blob: d82d1ad47e4aabb00267d2ac37653bb143f9cfc0 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2012, 2016 Mentor Graphics and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Mentor Graphics - Initial API and implementation
* Marc Khouzam (Ericsson) - Run tests in alphabetical order since they are dependent on each other.
*******************************************************************************/
package org.eclipse.cdt.tests.dsf.gdb.tests;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
import org.eclipse.cdt.debug.core.breakpointactions.BreakpointActionManager;
import org.eclipse.cdt.debug.core.model.ICTracepoint;
import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor;
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.debug.service.IBreakpoints;
import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointDMContext;
import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext;
import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants;
import org.eclipse.cdt.dsf.gdb.internal.tracepointactions.CollectAction;
import org.eclipse.cdt.dsf.gdb.internal.tracepointactions.EvaluateAction;
import org.eclipse.cdt.dsf.gdb.internal.tracepointactions.ITracepointAction;
import org.eclipse.cdt.dsf.gdb.internal.tracepointactions.TracepointActionManager;
import org.eclipse.cdt.dsf.gdb.service.IGDBTraceControl;
import org.eclipse.cdt.dsf.gdb.service.IGDBTraceControl.ITraceStatusDMData;
import org.eclipse.cdt.dsf.gdb.service.IGDBTraceControl.ITraceTargetDMContext;
import org.eclipse.cdt.dsf.mi.service.MIBreakpoints;
import org.eclipse.cdt.dsf.mi.service.MIBreakpoints.MIBreakpointDMContext;
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.DsfServicesTracker;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.cdt.tests.dsf.gdb.framework.AsyncCompletionWaitor;
import org.eclipse.cdt.tests.dsf.gdb.framework.BaseParametrizedTestCase;
import org.eclipse.cdt.tests.dsf.gdb.framework.SyncUtil;
import org.eclipse.cdt.tests.dsf.gdb.launching.TestsPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.Path;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.IBreakpointManager;
import org.eclipse.debug.core.model.IBreakpoint;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class TraceFileTest extends BaseParametrizedTestCase {
private final static String SOURCE_NAME = "TracepointTestApp.cc";
private final static String EXEC_NAME = "TracepointTestApp.exe";
private final static String TRACE_NAME = "trace";
private final static String TRACE_FILE_PATH = EXEC_PATH + TRACE_NAME;
// Breakpoint tags in TracepointTestApp.cc
public static final String[] LINE_TAGS = new String[] { "IF_X_NE_A", "INCR_X", };
private final static String END_FUNCTION = "lastCall";
private final static String TEVAL_STRING = "a";
private final static String COLLECT_STRING1 = "x";
private final static String COLLECT_STRING2 = "$regs";
private DsfSession fSession;
private DsfServicesTracker fServicesTracker;
private IBreakpoints fBreakpointService;
private IGDBTraceControl fTraceService;
private IBreakpointsTargetDMContext fBreakpointsDmc;
private ITraceTargetDMContext fTraceTargetDmc;
private boolean suppressRemoveAllPlatformBreakpoints;
@Override
public void doBeforeTest() throws Exception {
// GDB tracepoints are only supported on a remote target (e.g., using gdbserver)
assumeRemoteSession();
resolveLineTagLocations(SOURCE_NAME, LINE_TAGS);
assumeGdbVersionAtLeast(ITestConstants.SUFFIX_GDB_7_4);
teminateAndRemoveLaunches();
// Suppress settings of the launch attributes and launching.
// Each test sets its own launch attributes
}
@AfterClass
public static void doAfterClassTraceFileTest_7_4() {
try {
// Make sure we don't have any tracepoint actions
// or any kind of breakpoints in the workspace
// so that tests run after this class are not affected
deleteActionsAndBreakpoints();
} catch (Throwable e) {
System.out.println("ERROR: Failed to delete all breakpoints");
}
}
/**
* Some tests call doBefore/After in the middle of their test and rely on
* platform breakpoints to survive that step. So override with the ability
* to disable.
*/
@Override
public void removeAllPlatformBreakpoints() throws CoreException {
if (!suppressRemoveAllPlatformBreakpoints) {
super.removeAllPlatformBreakpoints();
}
}
@Override
@After
public void doAfterTest() throws Exception {
fBreakpointService = null;
fTraceService = null;
fBreakpointsDmc = null;
fTraceTargetDmc = null;
if (fServicesTracker != null) {
fServicesTracker.dispose();
fServicesTracker = null;
}
super.doAfterTest();
}
/**
* This method implements the following steps.
* 1. Starts a remote session
* 2. Sets two tracepoints in data/launch/src/TracepointTestApp.cc
* The first tracepoint's command is "teval a".
* The second tracepoint's commands are "collect counter" and "collect $regs".
* 3. Sets a regular breakpoint at the end of the source file.
* 4. Starts tracing
* 5. Resumes and runs until the breakpoint is hit
* 6. Stops tracing
* 7. Saves the trace data into a file (data/launch/bin/trace).
*/
protected void createTraceFile() throws Throwable {
// Make sure that there are no tracepoint actions and no platform breakpoints in the workspace.
deleteActionsAndBreakpoints();
deleteOldTraceFile();
startRemoteSession();
setTracepoints();
MIBreakpointDMContext bptDMC = setBreakpointAtEndLine();
startTracing();
MIStoppedEvent stoppedEvent = SyncUtil.resumeUntilStopped();
assertTrue(stoppedEvent instanceof MIBreakpointHitEvent
&& ((MIBreakpointHitEvent) stoppedEvent).getNumber().equals(bptDMC.getReference()));
stopTracing();
saveTraceData();
}
/**
* This test verifies that creating a trace file works as expected.
* @see #createTraceFile
*/
@Test
public void testCreateTraceFile() throws Throwable {
// This tests forces a remote session by calling startRemoteSession()
// The problem is that if gdbserver is not available, the test will fail
// If the tests being run are only triggering local tests, then this failure
// should not be happening.
// Therefore, we only run this tests if we are specifically running remote tests.
Assume.assumeTrue("Skipping non-remote", remote);
createTraceFile();
}
/**
* This test sets up by first creating a trace file
* by calling {@link #createTraceFile}
*
* It then removes all existing tracepoint actions and tracepoints
* and verifies that corresponding platform tracepoints with the proper
* actions are created.
*/
@Test
public void testTraceFile() throws Throwable {
// This test will force a local post-mortem session, so only run it in local mode
Assume.assumeTrue("Skipping remote", !remote);
try {
createTraceFile();
suppressRemoveAllPlatformBreakpoints = true;
try {
// Cleanup the interim launch that we just caused
doAfterTest();
// Setup for the upcoming launch
doBeforeTest();
} finally {
suppressRemoveAllPlatformBreakpoints = false;
}
} catch (Throwable t) {
// If we cannot create the trace file, ignore the test using the
// assume check below. The reason for the failure could be a missing
// gdbserver, and we don't want to fail a local test due to that
}
Assume.assumeTrue("Cannot find trace file: " + TRACE_FILE_PATH, new File(TRACE_FILE_PATH).exists());
// Make sure that there are no tracepoint actions and no platform breakpoints in the workspace.
deleteActionsAndBreakpoints();
startTraceFileSession();
// Verify that required tracepoints and new tracepoint actions are created.
checkActionsAndTracepoints();
}
/**
* This test sets up by first creating a trace file and importing it back
* by calling {@link #testTraceFile} which also calls {@link #createTraceFile}.
* It then verifies that the tracepoint actions and platform tracepoints
* created by {@link #testTraceFile()} are associated with the corresponding target
* tracepoints and are not created a second time.
*/
@Test
public void testTraceFileWithExistingTracepoints() throws Throwable {
// This test will force a local post-mortem session, so only run it in local mode
Assume.assumeTrue("Skipping remote", !remote);
// This test requires the presence of tracepoints created by another test.
// To allow our current test to be independent, we explicitly call
// the required test ourselves.
testTraceFile();
suppressRemoveAllPlatformBreakpoints = true;
try {
// Cleanup the interim launch that we just caused
doAfterTest();
// Setup for the upcoming launch
clearLineTags();
doBeforeTest();
} finally {
suppressRemoveAllPlatformBreakpoints = false;
}
// Verify that actions and tracepoints required for this test are in place.
checkActionsAndTracepoints();
startTraceFileSession();
// Verify that no new platform tracepoints or new tracepoint actions are created.
checkActionsAndTracepoints();
}
private void startTraceFileSession() throws Throwable {
// Set launch attributes
super.setLaunchAttributes();
// Set a working directory for GDB that is different than eclipse's directory.
// This allows us to make sure we properly handle finding the core file,
// especially in the case of a relative path
setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_WORKING_DIRECTORY, "${workspace_loc}");
// Because we just set a different working directory, we must use an absolute path for the program
String absoluteProgram = new Path(EXEC_PATH + EXEC_NAME).toFile().getAbsolutePath();
setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, absoluteProgram);
// Set post-mortem launch
setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_START_MODE,
ICDTLaunchConfigurationConstants.DEBUGGER_MODE_CORE);
// Set post-mortem type to trace file
setLaunchAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_POST_MORTEM_TYPE,
IGDBLaunchConfigurationConstants.DEBUGGER_POST_MORTEM_TRACE_FILE);
// Set core file path
setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_COREFILE_PATH, TRACE_FILE_PATH);
doLaunch();
// Allow time to create tracepoint actions
Thread.sleep(1000);
}
/**
* Deletes all tracepoint actions and all existing platform breakpoints.
*/
private static void deleteActionsAndBreakpoints() throws Throwable {
TracepointActionManager tam = TracepointActionManager.getInstance();
IBreakpointManager bm = DebugPlugin.getDefault().getBreakpointManager();
// Delete all existing actions
@SuppressWarnings("unchecked")
ArrayList<ITracepointAction> actions = (ArrayList<ITracepointAction>) tam.getActions().clone();
for (ITracepointAction a : actions) {
tam.deleteAction(a);
}
IBreakpoint[] bpts = bm.getBreakpoints();
for (IBreakpoint b : bpts) {
bm.removeBreakpoint(b, true);
}
}
/**
* Checks whether there are only two platform tracepoints and three tracepoint actions.
*/
private void checkActionsAndTracepoints() throws Throwable {
TracepointActionManager tam = TracepointActionManager.getInstance();
IBreakpointManager bm = DebugPlugin.getDefault().getBreakpointManager();
ArrayList<ITracepointAction> actions = tam.getActions();
IBreakpoint[] bpts = bm.getBreakpoints();
actions = tam.getActions();
assertTrue(String.format("Unexpected count of tracepoint actions: %d", actions.size()), actions.size() == 3);
bpts = bm.getBreakpoints();
assertTrue(String.format("Unexpected count of breakpoints: %d", bpts.length), bpts.length == 2);
for (IBreakpoint b : bpts) {
assertTrue(b instanceof ICTracepoint);
checkTracepoint((ICTracepoint) b);
}
}
private void checkTracepoint(ICTracepoint tracepoint) throws Throwable {
TracepointActionManager tam = TracepointActionManager.getInstance();
assertTrue(SOURCE_NAME.equals(new Path(tracepoint.getFileName()).lastSegment()));
assertTrue(getLineForTag("IF_X_NE_A") == tracepoint.getLineNumber()
|| getLineForTag("INCR_X") == tracepoint.getLineNumber());
String[] actionNames = ((String) tracepoint.getMarker()
.getAttribute(BreakpointActionManager.BREAKPOINT_ACTION_ATTRIBUTE))
.split(TracepointActionManager.TRACEPOINT_ACTION_DELIMITER);
for (String name : actionNames) {
ITracepointAction a = tam.findAction(name);
assertNotNull(a);
if (a instanceof EvaluateAction) {
assertTrue(TEVAL_STRING.equals(((EvaluateAction) a).getEvalString()));
}
if (a instanceof CollectAction) {
assertTrue(COLLECT_STRING1.equals(((CollectAction) a).getCollectString())
|| COLLECT_STRING2.equals(((CollectAction) a).getCollectString()));
}
}
}
private void startRemoteSession() throws Throwable {
// Set launch attributes
super.setLaunchAttributes();
setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, EXEC_PATH + EXEC_NAME);
setLaunchAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_TRACEPOINT_MODE,
IGDBLaunchConfigurationConstants.DEBUGGER_TRACEPOINT_MODE_DEFAULT);
// Start the session
doLaunch();
// Initialize
fSession = getGDBLaunch().getSession();
Runnable runnable = () -> {
fServicesTracker = new DsfServicesTracker(TestsPlugin.getBundleContext(), fSession.getId());
fBreakpointService = fServicesTracker.getService(IBreakpoints.class);
fTraceService = fServicesTracker.getService(IGDBTraceControl.class);
};
fSession.getExecutor().submit(runnable).get();
IContainerDMContext containerDmc = SyncUtil.getContainerContext();
fBreakpointsDmc = DMContexts.getAncestorOfType(containerDmc, IBreakpointsTargetDMContext.class);
assertNotNull(fBreakpointsDmc);
fTraceTargetDmc = DMContexts.getAncestorOfType(containerDmc, ITraceTargetDMContext.class);
assertNotNull(fTraceTargetDmc);
}
private void startTracing() throws Throwable {
final AsyncCompletionWaitor wait = new AsyncCompletionWaitor();
fSession.getExecutor().submit(() -> fTraceService.getTraceStatus(fTraceTargetDmc,
new DataRequestMonitor<ITraceStatusDMData>(fSession.getExecutor(), null) {
@Override
@ConfinedToDsfExecutor("fExecutor")
protected void handleCompleted() {
if (isSuccess() && getData().isTracingSupported()) {
fTraceService.startTracing(fTraceTargetDmc,
new RequestMonitor(fSession.getExecutor(), null) {
@Override
@ConfinedToDsfExecutor("fExecutor")
protected void handleCompleted() {
wait.waitFinished(getStatus());
}
});
} else {
wait.waitFinished(getStatus());
}
}
}));
wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER);
assertTrue(wait.getMessage(), wait.isOK());
}
private void stopTracing() throws Throwable {
final AsyncCompletionWaitor wait = new AsyncCompletionWaitor();
fSession.getExecutor().submit(
() -> fTraceService.stopTracing(fTraceTargetDmc, new RequestMonitor(fSession.getExecutor(), null) {
@Override
@ConfinedToDsfExecutor("fExecutor")
protected void handleCompleted() {
wait.waitFinished(getStatus());
}
}));
wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER);
assertTrue(wait.getMessage(), wait.isOK());
}
private MIBreakpointDMContext setBreakpointAtEndLine() throws Throwable {
Map<String, Object> attributes = new HashMap<>();
attributes.put(MIBreakpoints.BREAKPOINT_TYPE, MIBreakpoints.BREAKPOINT);
attributes.put(MIBreakpoints.FILE_NAME, SOURCE_NAME);
attributes.put(MIBreakpoints.FUNCTION, END_FUNCTION);
IBreakpointDMContext bptDMC = insertBreakpoint(fBreakpointsDmc, attributes);
assertTrue(bptDMC instanceof MIBreakpointDMContext);
return (MIBreakpointDMContext) bptDMC;
}
private void setTracepoints() throws Throwable {
TracepointActionManager tam = TracepointActionManager.getInstance();
CollectAction collectAction1 = new CollectAction();
collectAction1.setCollectString(COLLECT_STRING1);
collectAction1.setName(String.format("Collect %s", COLLECT_STRING1));
tam.addAction(collectAction1);
CollectAction collectAction2 = new CollectAction();
collectAction2.setCollectString(COLLECT_STRING2);
collectAction2.setName(String.format("Collect %s", COLLECT_STRING2));
tam.addAction(collectAction2);
EvaluateAction evalAction = new EvaluateAction();
evalAction.setEvalString(TEVAL_STRING);
evalAction.setName(String.format("Evaluate %s", TEVAL_STRING));
tam.addAction(evalAction);
Map<String, Object> attributes = new HashMap<>();
attributes.put(MIBreakpoints.BREAKPOINT_TYPE, MIBreakpoints.TRACEPOINT);
attributes.put(MIBreakpoints.FILE_NAME, SOURCE_NAME);
attributes.put(MIBreakpoints.LINE_NUMBER, getLineForTag("IF_X_NE_A"));
attributes.put(MIBreakpoints.COMMANDS, evalAction.getName());
insertBreakpoint(fBreakpointsDmc, attributes);
attributes.put(MIBreakpoints.LINE_NUMBER, getLineForTag("INCR_X"));
attributes.put(MIBreakpoints.COMMANDS, String.format("%s%s%s", collectAction1.getName(),
TracepointActionManager.TRACEPOINT_ACTION_DELIMITER, collectAction2.getName()));
insertBreakpoint(fBreakpointsDmc, attributes);
}
private IBreakpointDMContext insertBreakpoint(final IBreakpointsTargetDMContext context,
final Map<String, Object> attributes) throws InterruptedException {
final AsyncCompletionWaitor wait = new AsyncCompletionWaitor();
fSession.getExecutor().submit(() -> fBreakpointService.insertBreakpoint(context, attributes,
new DataRequestMonitor<IBreakpointDMContext>(fBreakpointService.getExecutor(), null) {
@Override
protected void handleCompleted() {
wait.setReturnInfo(getData());
wait.waitFinished(getStatus());
}
}));
// Wait for the result and return the breakpoint context
wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER);
assertTrue(wait.getMessage(), wait.isOK());
return (IBreakpointDMContext) wait.getReturnInfo();
}
private void saveTraceData() throws Throwable {
final File traceFile = new Path(TRACE_FILE_PATH).toFile();
final AsyncCompletionWaitor wait = new AsyncCompletionWaitor();
fSession.getExecutor().submit(() -> fTraceService.saveTraceData(fTraceTargetDmc, traceFile.getAbsolutePath(),
false, new RequestMonitor(fSession.getExecutor(), null) {
@Override
protected void handleCompleted() {
wait.waitFinished(getStatus());
}
}));
// Wait for the result and verify the trace file is created
wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER);
assertTrue(wait.getMessage(), wait.isOK());
assertTrue(traceFile.exists());
}
private void deleteOldTraceFile() throws Throwable {
File traceFile = new Path(TRACE_FILE_PATH).toFile();
traceFile.delete();
assertFalse(traceFile.exists());
}
}