/******************************************************************************* * Copyright (c) 2008, 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.assertFalse; import static org.junit.Assert.assertTrue; import java.math.BigInteger; 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.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.IFormattedValues.FormattedValueDMContext; import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMData; 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.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.cdt.utils.Addr64; 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 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(BackgroundRunner.class) public class MIDisassemblyTest extends BaseTestCase { private static final String FILE_NAME = "MemoryTestApp.cc"; private static final int LINE_NUMBER = 35; private static final String INVALID_FILE_NAME = "invalid_filename"; private final AsyncCompletionWaitor fWait = new AsyncCompletionWaitor(); private DsfSession fSession; private DsfServicesTracker fServicesTracker; private IDisassemblyDMContext fDisassemblyDmc; private MIDisassembly fDisassembly; private IExpressions fExpressionService; // ======================================================================== // Housekeeping stuff // ======================================================================== @BeforeClass public static void testSuiteInitialization() { // Select the binary to run the tests against setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, "data/launch/bin/MemoryTestApp.exe"); } @AfterClass public static void testSuiteCleanup() { } @Before public void testCaseInitialization() throws Exception { fSession = getGDBLaunch().getSession(); Runnable runnable = new Runnable() { 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); } @After public void testCaseCleanup() { fExpressionService = null; fDisassembly = 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 frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); // Create the expression and format contexts final IExpressionDMContext expressionDMC = SyncUtil.createExpression(frameDmc, expression); final FormattedValueDMContext formattedValueDMC = SyncUtil.getFormattedValue(fExpressionService, expressionDMC, IFormattedValues.HEX_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) fSession.getExecutor().submit(new Runnable() { public void run() { fExpressionService.getFormattedExpressionValue(formattedValueDMC, drm); } }); // Wait for completion fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); 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 Addr64(result); } /* ------------------------------------------------------------------------ * 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 void getInstruction(final IDisassemblyDMContext dmc, final BigInteger startAddress, final BigInteger endAddress) throws InterruptedException { // Set the Data Request Monitor final DataRequestMonitor<IInstruction[]> drm = new DataRequestMonitor<IInstruction[]>(fSession.getExecutor(), null) { @Override protected void handleCompleted() { if (isSuccess()) { fWait.setReturnInfo(getData()); } fWait.waitFinished(getStatus()); } }; // Issue the get memory request fSession.getExecutor().submit(new Runnable() { public void run() { fDisassembly.getInstructions(dmc, startAddress, endAddress, drm); } }); } /* ------------------------------------------------------------------------ * 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 void getInstruction(final IDisassemblyDMContext dmc, final String function, final int linenum, final int count) throws InterruptedException { // Set the Data Request Monitor final DataRequestMonitor<IInstruction[]> drm = new DataRequestMonitor<IInstruction[]>(fSession.getExecutor(), null) { @Override protected void handleCompleted() { if (isSuccess()) { fWait.setReturnInfo(getData()); } fWait.waitFinished(getStatus()); } }; // Issue the get memory request fSession.getExecutor().submit(new Runnable() { public void run() { fDisassembly.getInstructions(dmc, function, linenum, count, drm); } }); } /* ------------------------------------------------------------------------ * 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 void getMixedInstruction(final IDisassemblyDMContext dmc, final BigInteger startAddress, final BigInteger endAddress) throws InterruptedException { // Set the Data Request Monitor final DataRequestMonitor<IMixedInstruction[]> drm = new DataRequestMonitor<IMixedInstruction[]>(fSession.getExecutor(), null) { @Override protected void handleCompleted() { if (isSuccess()) { fWait.setReturnInfo(getData()); } fWait.waitFinished(getStatus()); } }; // Issue the get memory request fSession.getExecutor().submit(new Runnable() { public void run() { fDisassembly.getMixedInstructions(dmc, startAddress, endAddress, drm); } }); } /* ------------------------------------------------------------------------ * 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 void getMixedInstruction(final IDisassemblyDMContext dmc, final String function, final int linenum, final int count) throws InterruptedException { // Set the Data Request Monitor final DataRequestMonitor<IMixedInstruction[]> drm = new DataRequestMonitor<IMixedInstruction[]>(fSession.getExecutor(), null) { @Override protected void handleCompleted() { if (isSuccess()) { fWait.setReturnInfo(getData()); } fWait.waitFinished(getStatus()); } }; // Issue the get memory request fSession.getExecutor().submit(new Runnable() { public void run() { fDisassembly.getMixedInstructions(dmc, function, linenum, count, drm); } }); } // ======================================================================== // 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; // Perform the test fWait.waitReset(); getInstruction(null, startAddress, endAddress); fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); // Verify the result String expected = "Unknown context type"; assertFalse(fWait.getMessage(), fWait.isOK()); assertTrue("Wrong error message: expected '" + expected + "', received '" + fWait.getMessage() + "'", fWait.getMessage().contains(expected)); } // ------------------------------------------------------------------------ // readWithInvalidAddress // ------------------------------------------------------------------------ @Test(timeout=20000) public void readWithInvalidAddress() throws Throwable { // Setup call parameters BigInteger startAddress = BigInteger.ZERO; BigInteger endAddress = null; // Perform the test fWait.waitReset(); getInstruction(fDisassemblyDmc, startAddress, endAddress); fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); // Verify the result String expected = "Cannot access memory at address"; assertFalse(fWait.getMessage(), fWait.isOK()); assertTrue("Wrong error message: expected '" + expected + "', received '" + fWait.getMessage() + "'", fWait.getMessage().contains(expected)); } // ------------------------------------------------------------------------ // readWithNullAddress // ------------------------------------------------------------------------ @Test(timeout=20000) public void readWithNullAddress() throws Throwable { // Setup call parameters BigInteger startAddress = null; BigInteger endAddress = null; // Perform the test fWait.waitReset(); getInstruction(fDisassemblyDmc, startAddress, endAddress); fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); // Verify the result assertTrue(fWait.getMessage(), fWait.isOK()); IInstruction[] result = (IInstruction[]) fWait.getReturnInfo(); assertTrue("No instruction retrieved", result.length != 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 fWait.waitReset(); getInstruction(fDisassemblyDmc, startAddress, endAddress); fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); // Verify the result assertTrue(fWait.getMessage(), fWait.isOK()); IInstruction[] result = (IInstruction[]) fWait.getReturnInfo(); assertTrue("No instruction retrieved", result.length != 0); } // ------------------------------------------------------------------------ // readWithInvalidFilename // ------------------------------------------------------------------------ @Test(timeout=20000) public void readWithValidFunction() throws Throwable { // Setup call parameters String filename = INVALID_FILE_NAME; int linenum = 1; int count = -1; // Perform the test fWait.waitReset(); getInstruction(fDisassemblyDmc, filename, linenum, count); fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); // Verify the result String expected = "Invalid filename"; assertFalse(fWait.getMessage(), fWait.isOK()); assertTrue("Wrong error message: expected '" + expected + "', received '" + fWait.getMessage() + "'", fWait.getMessage().contains(expected)); } // ------------------------------------------------------------------------ // readWithInvalidLineNumber // ------------------------------------------------------------------------ @Test(timeout=20000) public void readWithInvalidLineNumber() throws Throwable { // Setup call parameters String filename = FILE_NAME; int linenum = -1; int count = -1; // Perform the test fWait.waitReset(); getInstruction(fDisassemblyDmc, filename, linenum, count); fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); // Verify the result String expected = "Invalid line number"; assertFalse(fWait.getMessage(), fWait.isOK()); assertTrue("Wrong error message: expected '" + expected + "', received '" + fWait.getMessage() + "'", fWait.getMessage().contains(expected)); } // ------------------------------------------------------------------------ // readWithValidFilename // ------------------------------------------------------------------------ @Test(timeout=20000) public void readWithValidFilename() throws Throwable { // Setup call parameters String filename = FILE_NAME; int linenum = LINE_NUMBER; int count = -1; // Perform the test fWait.waitReset(); getInstruction(fDisassemblyDmc, filename, linenum, count); fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); // Verify the result assertTrue(fWait.getMessage(), fWait.isOK()); IInstruction[] result = (IInstruction[]) fWait.getReturnInfo(); assertTrue("No instruction retrieved", result.length != 0); } // ------------------------------------------------------------------------ // readWithLineCount // ------------------------------------------------------------------------ @Test(timeout=20000) public void readWithLineCount() throws Throwable { // Setup call parameters String filename = FILE_NAME; int linenum = LINE_NUMBER; int count = 5; // Perform the test fWait.waitReset(); getInstruction(fDisassemblyDmc, filename, linenum, count); fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); // Verify the result assertTrue(fWait.getMessage(), fWait.isOK()); IInstruction[] result = (IInstruction[]) fWait.getReturnInfo(); assertTrue("Wrong number of instructions retrieved, expected " + count + ", got " + result.length, result.length == 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 fWait.waitReset(); getMixedInstruction(fDisassemblyDmc, startAddress, endAddress); fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); // Verify the result assertTrue(fWait.getMessage(), fWait.isOK()); IMixedInstruction[] result = (IMixedInstruction[]) fWait.getReturnInfo(); assertTrue("No instruction retrieved", result.length != 0); } // ------------------------------------------------------------------------ // readMixedWithLineCount // ------------------------------------------------------------------------ @Test(timeout=20000) public void readMixedWithLineCount() throws Throwable { // Setup call parameters String filename = FILE_NAME; int linenum = LINE_NUMBER; int count = 5; // Perform the test fWait.waitReset(); getMixedInstruction(fDisassemblyDmc, filename, linenum, count); fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); // Verify the result assertTrue(fWait.getMessage(), fWait.isOK()); IMixedInstruction[] result = (IMixedInstruction[]) fWait.getReturnInfo(); int total = 0; for (IMixedInstruction mixed : result) { IInstruction[] inst = mixed.getInstructions(); total += inst.length; } assertTrue("Wrong number of instructions retrieved, expected " + count + ", got " + result.length, total == count); } }