/******************************************************************************* * Copyright (c) 2007, 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 AB - Initial Implementation * Alvaro Sanchez-Leon (Ericsson AB) - [Memory] Support 16 bit addressable size (Bug 426730) * Alvaro Sanchez-Leon (Ericsson AB) - [Memory] Make tests run with different values of addressable size (Bug 460241) * Simon Marchi (Ericsson) - Refactoring, remove usage of AsyncCompletionWaitor *******************************************************************************/ package org.eclipse.cdt.tests.dsf.gdb.tests; import static org.hamcrest.CoreMatchers.anyOf; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; import java.nio.ByteBuffer; import java.nio.ByteOrder; 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.IDMContext; 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.IMemory; import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryChangedEvent; import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryDMContext; import org.eclipse.cdt.dsf.debug.service.IRunControl.StepType; import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; import org.eclipse.cdt.dsf.mi.service.MIRunControl; import org.eclipse.cdt.dsf.mi.service.command.events.MIStoppedEvent; import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; import org.eclipse.cdt.dsf.service.DsfServicesTracker; import org.eclipse.cdt.dsf.service.DsfSession; import org.eclipse.cdt.tests.dsf.gdb.framework.BaseParametrizedTestCase; import org.eclipse.cdt.tests.dsf.gdb.framework.MemoryByteBuffer; 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.cdt.utils.Addr64; import org.eclipse.debug.core.model.MemoryByte; 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 Memory 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 * Memory Service. * * Refer to the JUnit4 documentation for an explanation of the annotations. */ @RunWith(Parameterized.class) public class MIMemoryTest extends BaseParametrizedTestCase { private static final String EXEC_NAME = "MemoryTestApp.exe"; private DsfSession fSession; private DsfServicesTracker fServicesTracker; private IMemoryDMContext fMemoryDmc; private MIRunControl fRunControl; private IMemory fMemoryService; private IExpressions fExpressionService; private int fWordSize = 1 /* Default */; private ByteOrder fByteOrder; // Keeps track of the MemoryChangedEvents private final int BLOCK_SIZE = 256; private IAddress fBaseAddress; private Integer fMemoryChangedEventCount = 0; private boolean[] fMemoryAddressesChanged = new boolean[BLOCK_SIZE]; @Rule final public ExpectedException expectedException = ExpectedException.none(); // ======================================================================== // Housekeeping stuff // ======================================================================== @Override public void doBeforeTest() throws Exception { super.doBeforeTest(); fSession = getGDBLaunch().getSession(); fMemoryDmc = (IMemoryDMContext)SyncUtil.getContainerContext(); assert(fMemoryDmc != null); 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); fRunControl = fServicesTracker.getService(MIRunControl.class); assert(fRunControl != null); fMemoryService = fServicesTracker.getService(IMemory.class); assert(fMemoryService != null); fExpressionService = fServicesTracker.getService(IExpressions.class); assert(fExpressionService != null); fSession.addServiceEventListener(MIMemoryTest.this, null); fBaseAddress = null; clearEventCounters(); fWordSize = SyncUtil.readAddressableSize(fMemoryDmc); fByteOrder = SyncUtil.getMemoryByteOrder(fMemoryDmc); } }; fSession.getExecutor().submit(runnable).get(); } @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(); // Clear the references (not strictly necessary) if (fSession != null) fSession.getExecutor().submit(() -> fSession.removeServiceEventListener(MIMemoryTest.this)).get(); fBaseAddress = null; fExpressionService = null; fMemoryService = null; fRunControl = null; if (fServicesTracker != null) fServicesTracker.dispose(); fServicesTracker = null; clearEventCounters(); } // ======================================================================== // Helper Functions // ======================================================================== /* ------------------------------------------------------------------------ * eventDispatched * ------------------------------------------------------------------------ * Processes MemoryChangedEvents. * First checks if the memory block base address was set so the individual * test can control if it wants to verify the event(s). * ------------------------------------------------------------------------ * @param e The MemoryChangedEvent * ------------------------------------------------------------------------ */ @DsfServiceEventHandler public void eventDispatched(IMemoryChangedEvent e) { synchronized(fMemoryChangedEventCount) { fMemoryChangedEventCount++; } IAddress[] addresses = e.getAddresses(); for (int i = 0; i < addresses.length; i++) { int offset = Math.abs(addresses[i].distanceTo(fBaseAddress).intValue()); if (offset < BLOCK_SIZE) synchronized(fMemoryAddressesChanged) { fMemoryAddressesChanged[offset] = true; } } } // Clears the counters private void clearEventCounters() { synchronized(fMemoryChangedEventCount) { fMemoryChangedEventCount = 0; } synchronized(fMemoryAddressesChanged) { for (int i = 0; i < BLOCK_SIZE; i++) fMemoryAddressesChanged[i] = false; } } // Returns the total number of events received private int getEventCount() { int count; synchronized(fMemoryChangedEventCount) { count = fMemoryChangedEventCount; } return count; } // Returns the number of distinct addresses reported private int getAddressCount() { int count = 0; synchronized(fMemoryAddressesChanged) { for (int i = 0; i < BLOCK_SIZE; i++) if (fMemoryAddressesChanged[i]) count++; } return count; } private byte[] valueToBytes(int val) { ByteBuffer buff = ByteBuffer.allocate(fWordSize); switch (fWordSize) { case 1: byte bvalue = (byte) val; return buff.put(bvalue).array(); case 2: short svalue = (short) val; return buff.putShort(svalue).array(); case 4: return buff.putInt(val).array(); case 8: long lvalue = val; return buff.putLong(lvalue).array(); default: return null; } } /* ------------------------------------------------------------------------ * 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(IDMContext ctx, String expression) throws Throwable { IExpressionDMContext expressionDMC = SyncUtil.createExpression(ctx, expression); return new Addr64(SyncUtil.getExpressionValue(expressionDMC, IFormattedValues.HEX_FORMAT)); } // ======================================================================== // 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 that a null context is caught and generates an error // ------------------------------------------------------------------------ @Test public void readWithNullContext() throws Throwable { // Run to the point where the variable is initialized SyncUtil.runToLocation("MemoryTestApp.cc:zeroBlocks"); MIStoppedEvent stoppedEvent = SyncUtil.step(StepType.STEP_RETURN); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); // Setup call parameters IMemoryDMContext dmc = null; long offset = 0; int count = 1; fBaseAddress = evaluateExpression(frameDmc, "&charBlock"); expectedException.expect(ExecutionException.class); expectedException.expectMessage("Unknown context type"); // Perform the test try { SyncUtil.readMemory(dmc, fBaseAddress, offset, fWordSize, count); } finally { // Ensure no MemoryChangedEvent event was received assertEquals("MemoryChangedEvent problem: expected 0 events", 0, getEventCount()); } } // ------------------------------------------------------------------------ // readWithInvalidAddress // Test that an invalid address is caught and generates an error // ------------------------------------------------------------------------ @Test public void readWithInvalidAddress() throws Throwable { // Run to the point where the variable is initialized SyncUtil.runToLocation("MemoryTestApp.cc:zeroBlocks"); SyncUtil.step(StepType.STEP_RETURN); // Setup call parameters long offset = 0; int count = 1; fBaseAddress = new Addr64("0"); // Perform the test MemoryByte[] buffer = SyncUtil.readMemory(fMemoryDmc, fBaseAddress, offset, fWordSize, count); // Ensure that we receive a block of invalid memory bytes byte flags = MemoryByte.ENDIANESS_KNOWN; if (fByteOrder == ByteOrder.BIG_ENDIAN) { flags |= MemoryByte.BIG_ENDIAN; } assertThat(buffer[0].getValue(), is((byte) 0)); assertThat(buffer[0].getFlags(), is(flags)); // Ensure no MemoryChangedEvent event was received assertEquals("MemoryChangedEvent problem: expected 0 events", 0, getEventCount()); } // ------------------------------------------------------------------------ // readWithInvalidWordSize // Test that an invalid word size is caught and generates an error // ------------------------------------------------------------------------ @Test public void readWithInvalidWordSize() throws Throwable { // Run to the point where the variable is initialized SyncUtil.runToLocation("MemoryTestApp.cc:zeroBlocks"); MIStoppedEvent stoppedEvent = SyncUtil.step(StepType.STEP_RETURN); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); // Setup call parameters long offset = 0; int count = -1; fBaseAddress = evaluateExpression(frameDmc, "&charBlock"); expectedException.expect(ExecutionException.class); expectedException.expectMessage("Word size not supported (< 1)"); // Perform the test try { SyncUtil.readMemory(fMemoryDmc, fBaseAddress, offset, 0, count); } finally { // Ensure no MemoryChangedEvent event was received assertEquals("MemoryChangedEvent problem: expected 0 events", 0, getEventCount()); } } // ------------------------------------------------------------------------ // readWithInvalidCount // Test that an invalid count is caught and generates an error // ------------------------------------------------------------------------ @Test public void readWithInvalidCount() throws Throwable { // Run to the point where the variable is initialized SyncUtil.runToLocation("MemoryTestApp.cc:zeroBlocks"); MIStoppedEvent stoppedEvent = SyncUtil.step(StepType.STEP_RETURN); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); // Setup call parameters long offset = 0; int count = -1; fBaseAddress = evaluateExpression(frameDmc, "&charBlock"); // Perform the test expectedException.expect(ExecutionException.class); expectedException.expectMessage("Invalid word count (< 0)"); // Perform the test try { SyncUtil.readMemory(fMemoryDmc, fBaseAddress, offset, fWordSize, count); } finally { // Ensure no MemoryChangedEvent event was received assertEquals("MemoryChangedEvent problem: expected 0 events", 0, getEventCount()); } } // ------------------------------------------------------------------------ // readCharVaryingBaseAddress // Test the reading of individual bytes by varying the base address // ------------------------------------------------------------------------ @Test public void readCharVaryingBaseAddress() throws Throwable { // Run to the point where the variable is zeroed SyncUtil.runToLocation("MemoryTestApp.cc:zeroBlocks"); MIStoppedEvent stoppedEvent = SyncUtil.step(StepType.STEP_RETURN); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); // Setup call parameters int count = 1; fBaseAddress = evaluateExpression(frameDmc, "&charBlock"); // Verify that all bytes are '0' for (int i = 0; i < BLOCK_SIZE; i++) { IAddress address = fBaseAddress.add(i); MemoryByte[] buffer = SyncUtil.readMemory(fMemoryDmc, address, 0, fWordSize, count); MemoryByteBuffer memBuf = new MemoryByteBuffer(buffer, fByteOrder, fWordSize); assertThat(memBuf.getNextWord(), is(0L)); } // Run to the point where the variable is initialized SyncUtil.runToLocation("MemoryTestApp.cc:setBlocks"); SyncUtil.step(StepType.STEP_RETURN); // Verify that all bytes are set for (long i = 0; i < BLOCK_SIZE; i++) { IAddress address = fBaseAddress.add(i); MemoryByte[] buffer = SyncUtil.readMemory(fMemoryDmc, address, 0, fWordSize, count); MemoryByteBuffer memBuf = new MemoryByteBuffer(buffer, fByteOrder, fWordSize); assertThat(memBuf.getNextWord(), is(i)); } // Ensure no MemoryChangedEvent event was received assertEquals("MemoryChangedEvent problem: expected 0 events", 0, getEventCount()); } // ------------------------------------------------------------------------ // readCharVaryingOffset // Test the reading of individual bytes by varying the offset // ------------------------------------------------------------------------ @Test public void readCharVaryingOffset() throws Throwable { // Run to the point where the array is zeroed SyncUtil.runToLocation("MemoryTestApp.cc:zeroBlocks"); MIStoppedEvent stoppedEvent = SyncUtil.step(StepType.STEP_RETURN); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); // Setup call parameters int count = 1; fBaseAddress = evaluateExpression(frameDmc, "&charBlock"); // Verify that all bytes are '0' for (int offset = 0; offset < BLOCK_SIZE; offset++) { MemoryByte[] buffer = SyncUtil.readMemory(fMemoryDmc, fBaseAddress, offset, fWordSize, count); MemoryByteBuffer memBuf = new MemoryByteBuffer(buffer, fByteOrder, fWordSize); assertThat(memBuf.getNextWord(), is(0L)); } // Run to the point where the array is set SyncUtil.runToLocation("MemoryTestApp.cc:setBlocks"); SyncUtil.step(StepType.STEP_RETURN); // Verify that all bytes are set for (long offset = 0; offset < BLOCK_SIZE; offset++) { MemoryByte[] buffer = SyncUtil.readMemory(fMemoryDmc, fBaseAddress, offset, fWordSize, count); MemoryByteBuffer memBuf = new MemoryByteBuffer(buffer, fByteOrder, fWordSize); assertThat(memBuf.getNextWord(), is(offset)); } // Ensure no MemoryChangedEvent event was received assertEquals("MemoryChangedEvent problem: expected 0 events", 0, getEventCount()); } // ------------------------------------------------------------------------ // readCharArray // Test the reading of a byte array // ------------------------------------------------------------------------ @Test public void readCharArray() throws Throwable { // Run to the point where the variable is zeroed SyncUtil.runToLocation("MemoryTestApp.cc:zeroBlocks"); MIStoppedEvent stoppedEvent = SyncUtil.step(StepType.STEP_RETURN); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); // Setup call parameters int count = BLOCK_SIZE; fBaseAddress = evaluateExpression(frameDmc, "&charBlock"); // Get the memory block MemoryByte[] buffer = SyncUtil.readMemory(fMemoryDmc, fBaseAddress, 0, fWordSize, count); MemoryByteBuffer memBuf = new MemoryByteBuffer(buffer, fByteOrder, fWordSize); // Verify that all bytes are '0' for (int i = 0; i < count; i++) { assertThat(memBuf.getNextWord(), is(0L)); } // Run to the point where the variable is initialized SyncUtil.runToLocation("MemoryTestApp.cc:setBlocks"); SyncUtil.step(StepType.STEP_RETURN); // Get the memory block buffer = SyncUtil.readMemory(fMemoryDmc, fBaseAddress, 0, fWordSize, count); memBuf = new MemoryByteBuffer(buffer, fByteOrder, fWordSize); // Verify that all bytes are '0' for (long i = 0; i < count; i++) { assertThat(memBuf.getNextWord(), is(i)); } // Ensure no MemoryChangedEvent event was received assertEquals("MemoryChangedEvent problem: expected 0 events", 0, getEventCount()); } /////////////////////////////////////////////////////////////////////////// // setMemory tests /////////////////////////////////////////////////////////////////////////// // ------------------------------------------------------------------------ // writeWithNullContext // Test that a null context is caught and generates an error // ------------------------------------------------------------------------ @Test public void writeWithNullContext() throws Throwable { // Run to the point where the variable is initialized SyncUtil.runToLocation("MemoryTestApp.cc:zeroBlocks"); MIStoppedEvent stoppedEvent = SyncUtil.step(StepType.STEP_RETURN); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); // Setup call parameters long offset = 0; int count = 1; byte[] buffer = new byte[count * fWordSize]; fBaseAddress = evaluateExpression(frameDmc, "&charBlock"); expectedException.expect(ExecutionException.class); expectedException.expectMessage("Unknown context type"); // Perform the test try { SyncUtil.writeMemory(null, fBaseAddress, offset, fWordSize, count, buffer); } finally { // Ensure no MemoryChangedEvent event was received assertEquals("MemoryChangedEvent problem: expected 0 events", 0, getEventCount()); } } // ------------------------------------------------------------------------ // writeWithInvalidAddress // Test that an invalid address is caught and generates an error // ------------------------------------------------------------------------ @Test public void writeWithInvalidAddress() throws Throwable { // Run to the point where the variable is initialized SyncUtil.runToLocation("MemoryTestApp.cc:zeroBlocks"); SyncUtil.step(StepType.STEP_RETURN); // Setup call parameters long offset = 0; int count = 1; byte[] buffer = new byte[count * fWordSize]; fBaseAddress = new Addr64("0"); expectedException.expect(ExecutionException.class); String expectedStr1 = "Cannot access memory at address"; // Error message for new -data-write-memory-bytes command String expectedStr2 = "Could not write memory"; expectedException.expect(ExecutionException.class); expectedException.expectMessage(anyOf( containsString(expectedStr1), containsString(expectedStr2))); // Perform the test try { SyncUtil.writeMemory(fMemoryDmc, fBaseAddress, offset, fWordSize, count, buffer); } finally { // Ensure no MemoryChangedEvent event was received assertEquals("MemoryChangedEvent problem: expected 0 events", 0, getEventCount()); } } // ------------------------------------------------------------------------ // writeWithInvalidWordSize // Test that an invalid word size is caught and generates an error // ------------------------------------------------------------------------ @Test public void writeWithInvalidWordSize() throws Throwable { // Run to the point where the variable is initialized SyncUtil.runToLocation("MemoryTestApp.cc:zeroBlocks"); MIStoppedEvent stoppedEvent = SyncUtil.step(StepType.STEP_RETURN); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); // Setup call parameters long offset = 0; int count = -1; byte[] buffer = new byte[1]; fBaseAddress = evaluateExpression(frameDmc, "&charBlock"); expectedException.expect(ExecutionException.class); expectedException.expectMessage("Word size not supported (< 1)"); // Perform the test try { SyncUtil.writeMemory(fMemoryDmc, fBaseAddress, offset, 0, count, buffer); } finally { // Ensure no MemoryChangedEvent event was received assertEquals("MemoryChangedEvent problem: expected 0 events", 0, getEventCount()); } } // ------------------------------------------------------------------------ // writeWithInvalidCount // Test that an invalid count is caught and generates an error // ------------------------------------------------------------------------ @Test public void writeWithInvalidCount() throws Throwable { // Run to the point where the variable is initialized SyncUtil.runToLocation("MemoryTestApp.cc:zeroBlocks"); MIStoppedEvent stoppedEvent = SyncUtil.step(StepType.STEP_RETURN); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); // Setup call parameters long offset = 0; int count = -1; byte[] buffer = new byte[1]; fBaseAddress = evaluateExpression(frameDmc, "&charBlock"); expectedException.expect(ExecutionException.class); expectedException.expectMessage("Invalid word count (< 0)"); // Perform the test try { SyncUtil.writeMemory(fMemoryDmc, fBaseAddress, offset, fWordSize, count, buffer); } finally { // Ensure no MemoryChangedEvent event was received assertEquals("MemoryChangedEvent problem: expected 0 events", 0, getEventCount()); } } // ------------------------------------------------------------------------ // writeWithInvalidBuffer // Test that the buffer contains at least count bytes // ------------------------------------------------------------------------ @Test public void writeWithInvalidBuffer() throws Throwable { // Run to the point where the variable is initialized SyncUtil.runToLocation("MemoryTestApp.cc:zeroBlocks"); MIStoppedEvent stoppedEvent = SyncUtil.step(StepType.STEP_RETURN); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); // Setup call parameters long offset = 0; int count = 10; byte[] buffer = new byte[count * fWordSize - 1]; fBaseAddress = evaluateExpression(frameDmc, "&charBlock"); expectedException.expect(ExecutionException.class); expectedException.expectMessage("Buffer too short"); // Perform the test try { SyncUtil.writeMemory(fMemoryDmc, fBaseAddress, offset, fWordSize, count, buffer); } finally { // Ensure no MemoryChangedEvent event was received assertEquals("MemoryChangedEvent problem: expected 0 events", 0, getEventCount()); } } // ------------------------------------------------------------------------ // writeCharVaryingAddress // Test the writing of individual bytes by varying the base address // ------------------------------------------------------------------------ @Test public void writeCharVaryingAddress() throws Throwable { // Run to the point where the variable is zeroed SyncUtil.runToLocation("MemoryTestApp.cc:zeroBlocks"); MIStoppedEvent stoppedEvent = SyncUtil.step(StepType.STEP_RETURN); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); // Setup call parameters long offset = 0; int count = BLOCK_SIZE; //initialize write data buffer byte[] buffer; fBaseAddress = evaluateExpression(frameDmc, "&charBlock"); ServiceEventWaitor<IMemoryChangedEvent> eventWaitor = new ServiceEventWaitor<>( fSession, IMemoryChangedEvent.class); // Perform the test for (int i = 0; i < count; i++) { // [1] Ensure that the memory byte = 0 MemoryByte[] block = SyncUtil.readMemory(fMemoryDmc, fBaseAddress, i, fWordSize, 1); assertEquals("Wrong value read at offset " + i, (byte) 0, block[0].getValue()); // [2] Write a byte value (count - i - 1) IAddress address = fBaseAddress.add(i); byte expected = (byte) (count - i - 1); buffer = valueToBytes(expected); buffer[0] = expected; SyncUtil.writeMemory(fMemoryDmc, address, offset, fWordSize, 1, buffer); // [3] Verify that the correct MemoryChangedEvent was sent IMemoryChangedEvent event = eventWaitor.waitForEvent(TestsPlugin.massageTimeout(1000)); assertThat(event.getAddresses().length, is(1)); assertThat(event.getAddresses()[0], is(address)); // [4] Verify that the memory byte was written correctly block = SyncUtil.readMemory(fMemoryDmc, fBaseAddress, i, fWordSize, 1); assertEquals("Wrong value read at offset " + i, expected, block[0].getValue()); } // Ensure the MemoryChangedEvent events were received assertEquals("Incorrect count of MemoryChangedEvent", BLOCK_SIZE, getEventCount()); assertEquals("Incorrect count of events for distinct addresses", BLOCK_SIZE, getAddressCount()); } // ------------------------------------------------------------------------ // writeCharVaryingOffset // Test the writing of individual bytes by varying the base address // ------------------------------------------------------------------------ @Test public void writeCharVaryingOffset() throws Throwable { // Run to the point where the variable is zeroed SyncUtil.runToLocation("MemoryTestApp.cc:zeroBlocks"); MIStoppedEvent stoppedEvent = SyncUtil.step(StepType.STEP_RETURN); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); int count = BLOCK_SIZE; byte[] buffer; fBaseAddress = evaluateExpression(frameDmc, "&charBlock"); ServiceEventWaitor<IMemoryChangedEvent> eventWaitor = new ServiceEventWaitor<>( fSession, IMemoryChangedEvent.class); // Perform the test for (int offset = 0; offset < count; offset++) { // [1] Ensure that the memory byte = 0 MemoryByte[] block = SyncUtil.readMemory(fMemoryDmc, fBaseAddress, offset, fWordSize, 1); assertEquals("Wrong value read at offset " + offset, (byte) 0, block[0].getValue()); // [2] Write a byte value (count - offset - 1) byte expected = (byte) (count - offset - 1); buffer = valueToBytes(expected); buffer[0] = expected; SyncUtil.writeMemory(fMemoryDmc, fBaseAddress, offset, fWordSize, 1, buffer); // [3] Verify that the correct MemoryChangedEvent was sent IMemoryChangedEvent event = eventWaitor.waitForEvent(TestsPlugin.massageTimeout(1000)); assertThat(event.getAddresses().length, is(1)); assertThat(event.getAddresses()[0], is(fBaseAddress.add(offset))); // [4] Verify that the memory byte was written correctly block = SyncUtil.readMemory(fMemoryDmc, fBaseAddress, offset, fWordSize, 1); assertEquals("Wrong value read at offset " + offset, expected, block[0].getValue()); } // Ensure the MemoryChangedEvent events were received assertEquals("Incorrect count of MemoryChangedEvent", BLOCK_SIZE, getEventCount()); assertEquals("Incorrect count of events for distinct addresses", BLOCK_SIZE, getAddressCount()); } // ------------------------------------------------------------------------ // writeCharArray // Test the writing of a byte array // ------------------------------------------------------------------------ @Test public void writeCharArray() throws Throwable { // Run to the point where the variable is zeroed SyncUtil.runToLocation("MemoryTestApp.cc:zeroBlocks"); MIStoppedEvent stoppedEvent = SyncUtil.step(StepType.STEP_RETURN); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); // Setup call parameters int count = BLOCK_SIZE; fBaseAddress = evaluateExpression(frameDmc, "&charBlock"); // Make sure that the memory block is zeroed MemoryByte[] block = SyncUtil.readMemory(fMemoryDmc, fBaseAddress, 0, fWordSize, count); MemoryByteBuffer memBuf = new MemoryByteBuffer(block, fByteOrder, fWordSize); for (int i = 0; i < count; i++) { assertThat(memBuf.getNextWord(), is(0L)); } // Write an initialized memory block ByteBuffer buffer = ByteBuffer.allocate(count * fWordSize); for (int i = 0; i < count; i++) { buffer.put(valueToBytes(i)); } SyncUtil.writeMemory(fMemoryDmc, fBaseAddress, 0, fWordSize, count, buffer.array()); // Make sure that the memory block is initialized block = SyncUtil.readMemory(fMemoryDmc, fBaseAddress, 0, fWordSize, count); memBuf = new MemoryByteBuffer(block, fByteOrder, fWordSize); for (long i = 0; i < count; i++) { assertThat(memBuf.getNextWord(), is(i)); } // Ensure the MemoryChangedEvent events were received assertEquals("Incorrect count of MemoryChangedEvent", 1, getEventCount()); assertEquals("Incorrect count of events for distinct addresses", BLOCK_SIZE, getAddressCount()); } /////////////////////////////////////////////////////////////////////////// // fillMemory tests /////////////////////////////////////////////////////////////////////////// // ------------------------------------------------------------------------ // fillWithNullContext // Test that a null context is caught and generates an error // ------------------------------------------------------------------------ @Test public void fillWithNullContext() throws Throwable { // Run to the point where the variable is initialized SyncUtil.runToLocation("MemoryTestApp.cc:zeroBlocks"); MIStoppedEvent stoppedEvent = SyncUtil.step(StepType.STEP_RETURN); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); // Setup call parameters long offset = 0; int count = 1; byte[] pattern = new byte[count * fWordSize]; fBaseAddress = evaluateExpression(frameDmc, "&charBlock"); expectedException.expect(ExecutionException.class); expectedException.expectMessage("Unknown context type"); // Perform the test try { SyncUtil.fillMemory(null, fBaseAddress, offset, fWordSize, count, pattern); } finally { // Ensure no MemoryChangedEvent event was received assertEquals("Incorrect count of MemoryChangedEvent", 0, getEventCount()); } } // ------------------------------------------------------------------------ // fillWithInvalidAddress // Test that an invalid address is caught and generates an error // ------------------------------------------------------------------------ @Test public void fillWithInvalidAddress() throws Throwable { // Run to the point where the variable is initialized SyncUtil.runToLocation("MemoryTestApp.cc:zeroBlocks"); SyncUtil.step(StepType.STEP_RETURN); // Setup call parameters long offset = 0; int count = 1; byte[] pattern = valueToBytes(1); fBaseAddress = new Addr64("0"); // Depending on the GDB, a different command can be used. Both error message are valid. // Error message for -data-write-memory command String expectedStr1 = "Cannot access memory at address"; // Error message for new -data-write-memory-bytes command String expectedStr2 = "Could not write memory"; expectedException.expect(ExecutionException.class); expectedException.expectMessage(anyOf( containsString(expectedStr1), containsString(expectedStr2))); // Perform the test try { SyncUtil.fillMemory(fMemoryDmc, fBaseAddress, offset, fWordSize, count, pattern); } finally { // Ensure no MemoryChangedEvent event was received assertEquals("MemoryChangedEvent problem: expected 0 events", 0, getEventCount()); } } // ------------------------------------------------------------------------ // fillWithInvalidWordSize // Test that an invalid word size is caught and generates an error // ------------------------------------------------------------------------ @Test public void fillWithInvalidWordSize() throws Throwable { // Run to the point where the variable is initialized SyncUtil.runToLocation("MemoryTestApp.cc:zeroBlocks"); MIStoppedEvent stoppedEvent = SyncUtil.step(StepType.STEP_RETURN); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); // Setup call parameters long offset = 0; int count = 1; byte[] pattern = new byte[1]; fBaseAddress = evaluateExpression(frameDmc, "&charBlock"); expectedException.expect(ExecutionException.class); expectedException.expectMessage("Word size not supported (< 1)"); // Perform the test try { SyncUtil.fillMemory(fMemoryDmc, fBaseAddress, offset, 0, count, pattern); } finally { // Ensure no MemoryChangedEvent event was received assertEquals("MemoryChangedEvent problem: expected 0 events", 0, getEventCount()); } } // ------------------------------------------------------------------------ // fillWithInvalidCount // Test that an invalid count is caught and generates an error // ------------------------------------------------------------------------ @Test public void fillWithInvalidCount() throws Throwable { // Run to the point where the variable is initialized SyncUtil.runToLocation("MemoryTestApp.cc:zeroBlocks"); MIStoppedEvent stoppedEvent = SyncUtil.step(StepType.STEP_RETURN); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); // Setup call parameters long offset = 0; int count = -1; byte[] pattern = new byte[1]; fBaseAddress = evaluateExpression(frameDmc, "&charBlock"); expectedException.expect(ExecutionException.class); expectedException.expectMessage("Invalid repeat count (< 0)"); // Perform the test try { SyncUtil.fillMemory(fMemoryDmc, fBaseAddress, offset, fWordSize, count, pattern); } finally { // Ensure no MemoryChangedEvent event was received assertEquals("MemoryChangedEvent problem: expected 0 events", 0, getEventCount()); } } // ------------------------------------------------------------------------ // fillWithInvalidPattern // Test that an empty pattern is caught and generates an error // ------------------------------------------------------------------------ @Test public void fillWithInvalidPattern() throws Throwable { // Run to the point where the variable is initialized SyncUtil.runToLocation("MemoryTestApp.cc:zeroBlocks"); MIStoppedEvent stoppedEvent = SyncUtil.step(StepType.STEP_RETURN); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); // Setup call parameters long offset = 0; int count = 1; byte[] pattern = new byte[0]; fBaseAddress = evaluateExpression(frameDmc, "&charBlock"); expectedException.expect(ExecutionException.class); expectedException.expectMessage("Empty pattern"); // Perform the test try { SyncUtil.fillMemory(fMemoryDmc, fBaseAddress, offset, fWordSize, count, pattern); } finally { // Ensure no MemoryChangedEvent event was received assertEquals("MemoryChangedEvent problem: expected 0 events", 0, getEventCount()); } } // ------------------------------------------------------------------------ // writePatternVaryingAddress // Test the writing of the pattern by varying the base address // ------------------------------------------------------------------------ @Test public void writePatternVaryingAddress() throws Throwable { // Run to the point where the variable is zeroed SyncUtil.runToLocation("MemoryTestApp.cc:zeroBlocks"); MIStoppedEvent stoppedEvent = SyncUtil.step(StepType.STEP_RETURN); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); // Setup call parameters int repetitionCount = 1; int patternLen = 4; // Prepare the buffer ByteBuffer patternBuffer = ByteBuffer.allocate(patternLen * fWordSize); for (int i = 0; i < patternLen; i++) { patternBuffer.put(valueToBytes(i)); } byte[] pattern = patternBuffer.array(); fBaseAddress = evaluateExpression(frameDmc, "&charBlock"); // Ensure that the memory is zeroed MemoryByte[] block = SyncUtil.readMemory(fMemoryDmc, fBaseAddress, 0, fWordSize, repetitionCount * patternLen); MemoryByteBuffer memBuf = new MemoryByteBuffer(block, fByteOrder, fWordSize); for (int i = 0; i < (repetitionCount * patternLen); i++) { assertThat(memBuf.getNextWord(), is(0L)); } for (int i = 0; i < BLOCK_SIZE; i += patternLen) { IAddress address = fBaseAddress.add(i); SyncUtil.fillMemory(fMemoryDmc, address, 0, fWordSize, repetitionCount, pattern); } // Verify that the memory is correctly set block = SyncUtil.readMemory(fMemoryDmc, fBaseAddress, 0, fWordSize, repetitionCount * patternLen); memBuf = new MemoryByteBuffer(block, fByteOrder, fWordSize); for (long i = 0; i < repetitionCount; i++) { for (long j = 0; j < patternLen; j++) { assertThat(memBuf.getNextWord(), is(j)); } } // Ensure the MemoryChangedEvent events were received assertEquals("Incorrect count of MemoryChangedEvent", BLOCK_SIZE / patternLen, getEventCount()); assertEquals("Incorrect count of events for distinct addresses", BLOCK_SIZE, getAddressCount()); } // ------------------------------------------------------------------------ // writePatternVaryingOffset // Test the writing of the pattern by varying the base address // ------------------------------------------------------------------------ @Test public void writePatternVaryingOffset() throws Throwable { // Run to the point where the variable is zeroed SyncUtil.runToLocation("MemoryTestApp.cc:zeroBlocks"); MIStoppedEvent stoppedEvent = SyncUtil.step(StepType.STEP_RETURN); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); // Setup call parameters long offset = 0; int patternLength = 4; int patternRepetitionCount = BLOCK_SIZE / patternLength; ByteBuffer patternBuf = ByteBuffer.allocate(patternLength * fWordSize); for (int i = 0; i < patternLength; i++) { patternBuf.put(valueToBytes(i)); } byte[] pattern = patternBuf.array(); fBaseAddress = evaluateExpression(frameDmc, "&charBlock"); // Ensure that the memory is zeroed MemoryByte[] block = SyncUtil.readMemory(fMemoryDmc, fBaseAddress, offset, fWordSize, patternRepetitionCount * patternLength); MemoryByteBuffer memBuf = new MemoryByteBuffer(block, fByteOrder, fWordSize); for (int i = 0; i < (patternRepetitionCount * patternLength); i++) { assertThat(memBuf.getNextWord(), is(0L)); } for (int i = 0; i < patternRepetitionCount; i++) { offset = i * patternLength; SyncUtil.fillMemory(fMemoryDmc, fBaseAddress, offset, fWordSize, 1, pattern); } // Verify that the memory is correctly set block = SyncUtil.readMemory(fMemoryDmc, fBaseAddress, 0, fWordSize, patternRepetitionCount * patternLength); memBuf = new MemoryByteBuffer(block, fByteOrder, fWordSize); for (int i = 0; i < patternRepetitionCount; i++) for (long j = 0; j < patternLength; j++) { assertThat(memBuf.getNextWord(), is(j)); } // Ensure the MemoryChangedEvent events were received assertEquals("Incorrect count of MemoryChangedEvent", patternRepetitionCount, getEventCount()); assertEquals("Incorrect count of events for distinct addresses", BLOCK_SIZE, getAddressCount()); } // ------------------------------------------------------------------------ // writePatternCountTimes // Test the writing of the pattern [count] times // ------------------------------------------------------------------------ @Test public void writePatternCountTimes() throws Throwable { // Run to the point where the variable is zeroed SyncUtil.runToLocation("MemoryTestApp.cc:zeroBlocks"); MIStoppedEvent stoppedEvent = SyncUtil.step(StepType.STEP_RETURN); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); // Setup call parameters int maxPatternRepetitionCount = 64; int patternLength = 4; ByteBuffer mBuff = ByteBuffer.allocate(patternLength * fWordSize); for (int i = 0; i < patternLength; i++) { mBuff.put(valueToBytes(i)); } byte[] pattern = mBuff.array(); fBaseAddress = evaluateExpression(frameDmc, "&charBlock"); // Ensure that the memory is zeroed MemoryByte[] block = SyncUtil.readMemory(fMemoryDmc, fBaseAddress, 0, fWordSize, maxPatternRepetitionCount * patternLength); MemoryByteBuffer memBuf = new MemoryByteBuffer(block, fByteOrder, fWordSize); for (int i = 0; i < (maxPatternRepetitionCount * patternLength); i++) { assertThat(memBuf.getNextWord(), is(0L)); } // Write the pattern [count] times SyncUtil.fillMemory(fMemoryDmc, fBaseAddress, 0, fWordSize, maxPatternRepetitionCount, pattern); // Verify that the memory is correctly set block = SyncUtil.readMemory(fMemoryDmc, fBaseAddress, 0, fWordSize, maxPatternRepetitionCount * patternLength); memBuf = new MemoryByteBuffer(block, fByteOrder, fWordSize); for (int i = 0; i < maxPatternRepetitionCount; i++) { for (long j = 0; j < patternLength; j++) { assertThat(memBuf.getNextWord(), is(j)); } } // Ensure the MemoryChangedEvent events were received assertEquals("Incorrect count of MemoryChangedEvent", 1, getEventCount()); assertEquals("Incorrect count of events for distinct addresses", BLOCK_SIZE, getAddressCount()); } // ------------------------------------------------------------------------ // asynchronousReadWrite // Test the asynchronous reading/writing of individual bytes (varying offset) // ------------------------------------------------------------------------ @Test public void asynchronousReadWrite() throws Throwable { // Run to the point where the array is zeroed SyncUtil.runToLocation("MemoryTestApp.cc:zeroBlocks"); MIStoppedEvent stoppedEvent = SyncUtil.step(StepType.STEP_RETURN); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); // Setup call parameters fBaseAddress = evaluateExpression(frameDmc, "&charBlock"); // Verify asynchronously that all bytes are '0' MemoryReadQuery readQueries[] = new MemoryReadQuery[BLOCK_SIZE]; // Send many read queries for (int offset = 0; offset < BLOCK_SIZE; offset++) { readQueries[offset] = new MemoryReadQuery(fMemoryService, fMemoryDmc, fBaseAddress, offset, fWordSize, 1); fMemoryService.getExecutor().submit(readQueries[offset]); } // Wait for all the queries to finish for (int offset = 0; offset < BLOCK_SIZE; offset++) { MemoryByte[] data = readQueries[offset].get(); assertThat(data.length, is(1)); assertThat(data[0].getValue(), is((byte) 0)); } // Write asynchronously ServiceEventWaitor<IMemoryChangedEvent> eventWaitor = new ServiceEventWaitor<IMemoryChangedEvent>( fSession, IMemoryChangedEvent.class); MemoryWriteQuery writeQueries[] = new MemoryWriteQuery[BLOCK_SIZE]; for (int offset = 0; offset < BLOCK_SIZE; offset++) { byte[] block = valueToBytes(offset); writeQueries[offset] = new MemoryWriteQuery(fMemoryService, fMemoryDmc, fBaseAddress, offset, fWordSize, 1, block); fMemoryService.getExecutor().submit(writeQueries[offset]); } // Wait for all the queries to finish for (int offset = 0; offset < BLOCK_SIZE; offset++) { writeQueries[offset].get(); } // Expect BLOCK_SIZE "memory changed" events for (int i = 0; i < BLOCK_SIZE; i++) { eventWaitor.waitForEvent(TestsPlugin.massageTimeout(1000)); } // Verify asynchronously that all bytes are set // Send many read queries for (int offset = 0; offset < BLOCK_SIZE; offset++) { readQueries[offset] = new MemoryReadQuery(fMemoryService, fMemoryDmc, fBaseAddress, offset, fWordSize, 1); fMemoryService.getExecutor().submit(readQueries[offset]); } // Wait for all the queries to finish for (int offset = 0; offset < BLOCK_SIZE; offset++) { MemoryByte[] data = readQueries[offset].get(); assertThat(data.length, is(fWordSize)); MemoryByteBuffer mbb = new MemoryByteBuffer(data, fByteOrder, fWordSize); assertThat(mbb.getNextWord(), is((long) offset)); } } private void memoryCacheReadHelper(long offset, int count, int wordSize) throws InterruptedException, ExecutionException { MemoryByte[] buffer = SyncUtil.readMemory(fMemoryDmc, fBaseAddress, offset, wordSize, count); MemoryByteBuffer memBuf = new MemoryByteBuffer(buffer, fByteOrder, fWordSize); // Verify that all bytes are correctly set for (long i = 0; i < count; i++) { assertThat("index " + i, memBuf.getNextWord(), is(offset + i)); } } // ------------------------------------------------------------------------ // memoryCacheRead // Get a bunch of blocks to exercise the memory cache // ------------------------------------------------------------------------ @Test public void memoryCacheRead() throws Throwable { // Run to the point where the variable is initialized SyncUtil.runToLocation("MemoryTestApp.cc:setBlocks"); MIStoppedEvent stoppedEvent = SyncUtil.step(StepType.STEP_RETURN); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); // Setup call parameters fBaseAddress = evaluateExpression(frameDmc, "&charBlock"); // Get the 'reference' memory block memoryCacheReadHelper(0, BLOCK_SIZE, fWordSize); // Clear the cache SyncUtil.step(StepType.STEP_OVER); // Get a first block memoryCacheReadHelper(0, 64, fWordSize); // Get a second block memoryCacheReadHelper(128, 64, fWordSize); // Get a third block between the first 2 memoryCacheReadHelper(80, 32, fWordSize); // Get a block that is contiguous to the end of an existing block memoryCacheReadHelper(192, 32, fWordSize); // Get a block that ends beyond an existing block memoryCacheReadHelper(192, 64, fWordSize); // Get a block that will require 2 reads (for the gaps between blocks 1-2 and 2-3) memoryCacheReadHelper(32, 128, fWordSize); // Get a block that involves multiple cached blocks memoryCacheReadHelper(48, 192, fWordSize); // Get the whole block memoryCacheReadHelper(0, BLOCK_SIZE, fWordSize); // Ensure no MemoryChangedEvent event was received assertEquals("Incorrect count of MemoryChangedEvent", 0, getEventCount()); } private static class MemoryReadQuery extends Query<MemoryByte[]> { private IMemory fMemoryService; private IMemoryDMContext fMemoryDmc; private IAddress fBaseAddress; private int fOffset; private int fWordSize; private int fCount; public MemoryReadQuery(IMemory fMemoryService, IMemoryDMContext memoryDmc, IAddress baseAddress, int offset, int wordSize, int count) { this.fMemoryService = fMemoryService; this.fMemoryDmc = memoryDmc; this.fBaseAddress = baseAddress; this.fOffset = offset; this.fWordSize = wordSize; this.fCount = count; } @Override protected void execute(DataRequestMonitor<MemoryByte[]> rm) { fMemoryService.getMemory(fMemoryDmc, fBaseAddress, fOffset, fWordSize, fCount, rm); } } private static class MemoryWriteQuery extends Query<Void> { private IMemory fMemoryService; private IMemoryDMContext fMemoryDmc; private IAddress fBaseAddress; private int fOffset; private int fWordSize; private int fCount; private byte[] fBuffer; public MemoryWriteQuery(IMemory fMemoryService, IMemoryDMContext memoryDmc, IAddress baseAddress, int offset, int wordSize, int count, byte[] buffer) { this.fMemoryService = fMemoryService; this.fMemoryDmc = memoryDmc; this.fBaseAddress = baseAddress; this.fOffset = offset; this.fWordSize = wordSize; this.fCount = count; this.fBuffer = buffer; } @Override protected void execute(DataRequestMonitor<Void> rm) { fMemoryService.setMemory(fMemoryDmc, fBaseAddress, fOffset, fWordSize, fCount, fBuffer, rm); } } }