/******************************************************************************* * Copyright (c) 2008, 2015 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.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; import java.math.BigInteger; import java.util.concurrent.ExecutionException; import org.eclipse.cdt.core.IAddress; import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; import org.eclipse.cdt.dsf.concurrent.Query; import org.eclipse.cdt.dsf.datamodel.DMContexts; import org.eclipse.cdt.dsf.debug.service.IDisassembly.IDisassemblyDMContext; 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.IInstruction; import org.eclipse.cdt.dsf.debug.service.IMixedInstruction; import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; import org.eclipse.cdt.dsf.mi.service.MIDisassembly; 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.BaseParametrizedTestCase; import org.eclipse.cdt.tests.dsf.gdb.framework.SyncUtil; import org.eclipse.cdt.tests.dsf.gdb.launching.TestsPlugin; import org.eclipse.cdt.utils.Addr64; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; /* * This is the Disassembly Service test suite. * * 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 * Disassembly Service. * * Refer to the JUnit4 documentation for an explanation of the annotations. */ @RunWith(Parameterized.class) public class MIDisassemblyTest extends BaseParametrizedTestCase { private static final String EXEC_NAME = "MemoryTestApp.exe"; private static final String SOURCE_NAME = "MemoryTestApp.cc"; private static final String INVALID_SOURCE_NAME = "invalid_filename"; protected static final String[] LINE_TAGS = { "LINE_NUMBER", }; protected int LINE_NUMBER; private DsfSession fSession; private DsfServicesTracker fServicesTracker; private IDisassemblyDMContext fDisassemblyDmc; private MIDisassembly fDisassembly; private IExpressions fExpressionService; @Rule final public ExpectedException expectedException = ExpectedException.none(); // ======================================================================== // Housekeeping stuff // ======================================================================== @Override public void doBeforeTest() throws Exception { super.doBeforeTest(); fSession = getGDBLaunch().getSession(); Runnable runnable = new Runnable() { @Override public void run() { // Get a reference to the memory service fServicesTracker = new DsfServicesTracker(TestsPlugin.getBundleContext(), fSession.getId()); assert(fServicesTracker != null); fDisassembly = fServicesTracker.getService(MIDisassembly.class); assert(fDisassembly != null); fExpressionService = fServicesTracker.getService(IExpressions.class); assert(fExpressionService != null); } }; fSession.getExecutor().submit(runnable).get(); IContainerDMContext containerDmc = SyncUtil.getContainerContext(); fDisassemblyDmc = DMContexts.getAncestorOfType(containerDmc, IDisassemblyDMContext.class); assert(fDisassemblyDmc != null); resolveLineTagLocations(SOURCE_NAME, LINE_TAGS); LINE_NUMBER = getLineForTag("LINE_NUMBER"); } @Override protected void setLaunchAttributes() { super.setLaunchAttributes(); // Select the binary to run the tests against setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, EXEC_PATH + EXEC_NAME); } @Override public void doAfterTest() throws Exception { super.doAfterTest(); fExpressionService = null; fDisassembly = null; if (fServicesTracker != null) { fServicesTracker.dispose(); fServicesTracker = null; } } // ======================================================================== // 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 IAddress evaluateExpression(String expression) throws Throwable { MIStoppedEvent stoppedEvent = getInitialStoppedEvent(); IFrameDMContext ctx = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); IExpressionDMContext expressionDMC = SyncUtil.createExpression(ctx, expression); return new Addr64(SyncUtil.getExpressionValue(expressionDMC, IFormattedValues.HEX_FORMAT)); } /* ------------------------------------------------------------------------ * getInstruction * ------------------------------------------------------------------------ * Issues a disassembly request. The result is stored in fWait. * ------------------------------------------------------------------------ * Typical usage: * getInstruction(dmc, start, end); * fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); * assertTrue(fWait.getMessage(), fWait.isOK()); * ------------------------------------------------------------------------ * @param dmc the data model context * @param start the start address (null == $pc) * @param end the end address * @throws InterruptedException * ------------------------------------------------------------------------ */ private IInstruction[] getInstruction(final IDisassemblyDMContext dmc, final BigInteger startAddress, final BigInteger endAddress) throws InterruptedException, ExecutionException { Query<IInstruction[]> query = new Query<IInstruction[]>() { @Override protected void execute(DataRequestMonitor<IInstruction[]> rm) { fDisassembly.getInstructions(dmc, startAddress, endAddress, rm); } }; fDisassembly.getExecutor().submit(query); return query.get(); } /* ------------------------------------------------------------------------ * getInstruction * ------------------------------------------------------------------------ * Issues a disassembly request. The result is stored in fWait. * ------------------------------------------------------------------------ * Typical usage: * getInstruction(dmc, start, end); * fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); * assertTrue(fWait.getMessage(), fWait.isOK()); * ------------------------------------------------------------------------ * @param dmc the data model context * @param fucntion the function * @param linenum the line * @param count the instruction count * @throws InterruptedException * ------------------------------------------------------------------------ */ private IInstruction[] getInstruction(final IDisassemblyDMContext dmc, final String function, final int linenum, final int count) throws InterruptedException, ExecutionException { Query<IInstruction[]> query = new Query<IInstruction[]>() { @Override protected void execute(DataRequestMonitor<IInstruction[]> rm) { fDisassembly.getInstructions(dmc, function, linenum, count, rm); } }; fDisassembly.getExecutor().submit(query); return query.get(); } /* ------------------------------------------------------------------------ * getMixedInstruction * ------------------------------------------------------------------------ * Issues a disassembly request. The result is stored in fWait. * ------------------------------------------------------------------------ * Typical usage: * getInstruction(dmc, start, end); * fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); * assertTrue(fWait.getMessage(), fWait.isOK()); * ------------------------------------------------------------------------ * @param dmc the data model context * @param start the start address (null == $pc) * @param end the end address * @throws InterruptedException * ------------------------------------------------------------------------ */ private IMixedInstruction[] getMixedInstruction( final IDisassemblyDMContext dmc, final BigInteger startAddress, final BigInteger endAddress) throws InterruptedException, ExecutionException { Query<IMixedInstruction[]> query = new Query<IMixedInstruction[]>() { @Override protected void execute(DataRequestMonitor<IMixedInstruction[]> rm) { fDisassembly.getMixedInstructions(dmc, startAddress, endAddress, rm); } }; fDisassembly.getExecutor().submit(query); return query.get(); } /* ------------------------------------------------------------------------ * getMixedInstruction * ------------------------------------------------------------------------ * Issues a disassembly request. The result is stored in fWait. * ------------------------------------------------------------------------ * Typical usage: * getInstruction(dmc, start, end); * fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); * assertTrue(fWait.getMessage(), fWait.isOK()); * ------------------------------------------------------------------------ * @param dmc the data model context * @param start the start address (null == $pc) * @param end the end address * @throws InterruptedException * ------------------------------------------------------------------------ */ private IMixedInstruction[] getMixedInstruction( final IDisassemblyDMContext dmc, final String function, final int linenum, final int count) throws InterruptedException, ExecutionException { Query<IMixedInstruction[]> query = new Query<IMixedInstruction[]>() { @Override protected void execute(DataRequestMonitor<IMixedInstruction[]> rm) { fDisassembly.getMixedInstructions(dmc, function, linenum, count, rm); } }; fDisassembly.getExecutor().submit(query); return query.get(); } // ======================================================================== // Test Cases // ------------------------------------------------------------------------ // Templates: // ------------------------------------------------------------------------ // @ Test // public void basicTest() { // // First test to run // assertTrue("", true); // } // ------------------------------------------------------------------------ // @ Test(timeout=5000) // public void timeoutTest() { // // Second test to run, which will timeout if not finished on time // assertTrue("", true); // } // ------------------------------------------------------------------------ // @ Test(expected=FileNotFoundException.class) // public void exceptionTest() throws FileNotFoundException { // // Third test to run which expects an exception // throw new FileNotFoundException("Just testing"); // } // ======================================================================== /////////////////////////////////////////////////////////////////////////// // getMemory tests /////////////////////////////////////////////////////////////////////////// // ------------------------------------------------------------------------ // readWithNullContext // ------------------------------------------------------------------------ @Test(timeout=20000) public void readWithNullContext() throws Throwable { // Setup call parameters BigInteger startAddress = null; BigInteger endAddress = null; expectedException.expect(ExecutionException.class); expectedException.expectMessage("Unknown context type"); // Perform the test getInstruction(null, startAddress, endAddress); } // ------------------------------------------------------------------------ // readWithInvalidAddress // ------------------------------------------------------------------------ @Test(timeout=20000) public void readWithInvalidAddress() throws Throwable { // Setup call parameters BigInteger startAddress = BigInteger.ZERO; BigInteger endAddress = null; expectedException.expect(ExecutionException.class); expectedException.expectMessage("Cannot access memory at address"); // Perform the test getInstruction(fDisassemblyDmc, startAddress, endAddress); } // ------------------------------------------------------------------------ // readWithNullAddress // ------------------------------------------------------------------------ @Test(timeout=20000) public void readWithNullAddress() throws Throwable { // Setup call parameters BigInteger startAddress = null; BigInteger endAddress = null; // Perform the test IInstruction[] result = getInstruction(fDisassemblyDmc, startAddress, endAddress); // Verify the result assertThat(result.length, is(not(0))); } // ------------------------------------------------------------------------ // readWithValidAddress // ------------------------------------------------------------------------ @Test(timeout=20000) public void readWithValidAddress() throws Throwable { // Setup call parameters Addr64 main = (Addr64) evaluateExpression("&main"); BigInteger startAddress = main.getValue(); BigInteger endAddress = null; // Perform the test IInstruction[] result = getInstruction(fDisassemblyDmc, startAddress, endAddress); // Verify the result assertThat(result.length, is(not(0))); } // ------------------------------------------------------------------------ // readWithInvalidFilename // ------------------------------------------------------------------------ @Test(timeout=20000) public void readWithInvalidFilename() throws Throwable { // Setup call parameters String filename = INVALID_SOURCE_NAME; int linenum = 1; int count = -1; expectedException.expect(ExecutionException.class); expectedException.expectMessage("Invalid filename"); // Perform the test getInstruction(fDisassemblyDmc, filename, linenum, count); } // ------------------------------------------------------------------------ // readWithInvalidLineNumber // ------------------------------------------------------------------------ @Test(timeout=20000) public void readWithInvalidLineNumber() throws Throwable { // Setup call parameters String filename = SOURCE_NAME; int linenum = -1; int count = -1; expectedException.expect(ExecutionException.class); expectedException.expectMessage("Invalid line number"); // Perform the test getInstruction(fDisassemblyDmc, filename, linenum, count); } // ------------------------------------------------------------------------ // readWithValidFilename // ------------------------------------------------------------------------ @Test(timeout=20000) public void readWithValidFilename() throws Throwable { // Setup call parameters String filename = SOURCE_NAME; int linenum = LINE_NUMBER; int count = -1; // Perform the test IInstruction[] result = getInstruction(fDisassemblyDmc, filename, linenum, count); // Verify the result assertThat(result.length, is(not(0))); } // ------------------------------------------------------------------------ // readWithLineCount // ------------------------------------------------------------------------ @Test(timeout=20000) public void readWithLineCount() throws Throwable { // Setup call parameters String filename = SOURCE_NAME; int linenum = LINE_NUMBER; int count = 5; // Perform the test IInstruction[] result = getInstruction(fDisassemblyDmc, filename, linenum, count); // Verify the result assertThat(result.length, is(count)); } // ------------------------------------------------------------------------ // readMixedWithValidAddress // ------------------------------------------------------------------------ @Test(timeout=20000) public void readMixedWithValidAddress() throws Throwable { // Setup call parameters Addr64 main = (Addr64) evaluateExpression("&main"); BigInteger startAddress = main.getValue(); BigInteger endAddress = null; // Perform the test IMixedInstruction[] result = getMixedInstruction(fDisassemblyDmc, startAddress, endAddress); // Verify the result assertThat(result.length, is(not(0))); } // ------------------------------------------------------------------------ // readMixedWithLineCount // ------------------------------------------------------------------------ @Test(timeout=20000) public void readMixedWithLineCount() throws Throwable { // Setup call parameters String filename = SOURCE_NAME; int linenum = LINE_NUMBER; int count = 5; // Perform the test IMixedInstruction[] result = getMixedInstruction(fDisassemblyDmc, filename, linenum, count); // Verify the result int total = 0; for (IMixedInstruction mixed : result) { IInstruction[] inst = mixed.getInstructions(); total += inst.length; } assertThat(total, is(count)); } }