From 071016cea4c9942b7f5c26f4b7f0bbeb8ca37baf Mon Sep 17 00:00:00 2001 From: Daniel Kirschten Date: Sun, 29 Sep 2019 18:26:04 +0200 Subject: [PATCH] Made main memory access via the Memory view work --- plugins/net.mograsim.plugin.core/plugin.xml | 8 + .../plugin/launch/MachineDebugTarget.java | 11 +- .../plugin/launch/MachineProcess.java | 21 +- .../plugin/launch/MainMemoryBlock.java | 75 +++- .../launch/MainMemoryBlockExtension.java | 348 ++++++++++++++++++ 5 files changed, 433 insertions(+), 30 deletions(-) create mode 100644 plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MainMemoryBlockExtension.java diff --git a/plugins/net.mograsim.plugin.core/plugin.xml b/plugins/net.mograsim.plugin.core/plugin.xml index 1120682a..d2910481 100644 --- a/plugins/net.mograsim.plugin.core/plugin.xml +++ b/plugins/net.mograsim.plugin.core/plugin.xml @@ -391,4 +391,12 @@ id="net.mograsim.plugin.core.launchmachine.image"> + + + + diff --git a/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineDebugTarget.java b/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineDebugTarget.java index 5270c767..357fe82b 100644 --- a/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineDebugTarget.java +++ b/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineDebugTarget.java @@ -15,6 +15,8 @@ import org.eclipse.debug.core.model.IBreakpoint; import org.eclipse.debug.core.model.IDebugElement; import org.eclipse.debug.core.model.IDebugTarget; import org.eclipse.debug.core.model.IMemoryBlock; +import org.eclipse.debug.core.model.IMemoryBlockExtension; +import org.eclipse.debug.core.model.IMemoryBlockRetrievalExtension; import org.eclipse.debug.core.model.IProcess; import org.eclipse.debug.core.model.IStepFilters; import org.eclipse.debug.core.model.IThread; @@ -22,7 +24,7 @@ import org.eclipse.debug.core.model.IThread; import net.mograsim.machine.Machine; import net.mograsim.plugin.MograsimActivator; -public class MachineDebugTarget extends PlatformObject implements IDebugTarget +public class MachineDebugTarget extends PlatformObject implements IDebugTarget, IMemoryBlockRetrievalExtension { private final MachineProcess process; @@ -183,12 +185,19 @@ public class MachineDebugTarget extends PlatformObject implements IDebugTarget return true; } + @SuppressWarnings("deprecation") // TODO can we throw a DebugException instead? @Override public IMemoryBlock getMemoryBlock(long startAddress, long length) throws DebugException { return new MainMemoryBlock(this, startAddress, length); } + @Override + public IMemoryBlockExtension getExtendedMemoryBlock(String expression, Object context) throws DebugException + { + return new MainMemoryBlockExtension(this, expression, context); + } + @Override public MachineProcess getProcess() { diff --git a/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineProcess.java b/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineProcess.java index b22d9c5c..f3866902 100644 --- a/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineProcess.java +++ b/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineProcess.java @@ -90,11 +90,9 @@ public class MachineProcess extends PlatformObject implements IProcess, ISuspend public void suspend() throws DebugException { if (isTerminated()) - throw new DebugException(new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, DebugException.TARGET_REQUEST_FAILED, - "Can't suspend a terminated MachineProcess", null)); + throwDebugException("Can't suspend a terminated MachineProcess"); if (isSuspended()) - throw new DebugException(new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, DebugException.TARGET_REQUEST_FAILED, - "Can't suspend a suspended MachineProcess", null)); + throwDebugException("Can't suspend a suspended MachineProcess"); exec.pauseLiveExecution(); fireSuspendEvent(DebugEvent.CLIENT_REQUEST); @@ -110,11 +108,9 @@ public class MachineProcess extends PlatformObject implements IProcess, ISuspend public void resume() throws DebugException { if (isTerminated()) - throw new DebugException(new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, DebugException.TARGET_REQUEST_FAILED, - "Can't resume a terminated MachineProcess", null)); + throwDebugException("Can't resume a terminated MachineProcess"); if (!isSuspended()) - throw new DebugException(new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, DebugException.TARGET_REQUEST_FAILED, - "Can't resume a non-suspended MachineProcess", null)); + throwDebugException("Can't resume a non-suspended MachineProcess"); exec.unpauseLiveExecution(); fireResumeEvent(DebugEvent.CLIENT_REQUEST); @@ -147,8 +143,7 @@ public class MachineProcess extends PlatformObject implements IProcess, ISuspend public int getExitValue() throws DebugException { if (!isTerminated()) - throw new DebugException(new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, DebugException.TARGET_REQUEST_FAILED, - "Can't get the exit value of a running process", null)); + throwDebugException("Can't get the exit value of a running process"); return 0; } @@ -262,4 +257,10 @@ public class MachineProcess extends PlatformObject implements IProcess, ISuspend if (manager != null) manager.fireDebugEventSet(new DebugEvent[] { event }); } + + private static void throwDebugException(String message) throws DebugException + { + throw new DebugException( + new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, DebugException.TARGET_REQUEST_FAILED, message, null)); + } } \ No newline at end of file diff --git a/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MainMemoryBlock.java b/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MainMemoryBlock.java index b068fc09..c2edf4ec 100644 --- a/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MainMemoryBlock.java +++ b/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MainMemoryBlock.java @@ -1,6 +1,11 @@ package net.mograsim.plugin.launch; +import java.math.BigInteger; + +import org.eclipse.core.runtime.PlatformObject; +import org.eclipse.debug.core.DebugEvent; import org.eclipse.debug.core.DebugException; +import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.model.IDebugTarget; import org.eclipse.debug.core.model.IMemoryBlock; @@ -8,7 +13,8 @@ import org.eclipse.debug.core.model.IMemoryBlock; import net.mograsim.machine.MainMemory; import net.mograsim.plugin.MograsimActivator; -public class MainMemoryBlock implements IMemoryBlock +@Deprecated +public class MainMemoryBlock extends PlatformObject implements IMemoryBlock { private final MachineDebugTarget debugTarget; private final MainMemory mem; @@ -17,17 +23,29 @@ public class MainMemoryBlock implements IMemoryBlock public MainMemoryBlock(MachineDebugTarget debugTarget, long startAddress, long length) { + MainMemory mem = debugTarget.getMachine().getMainMemory(); + if (length < 0) - throw new IllegalArgumentException("Can't create a memory block of negative size"); + throw new IllegalArgumentException("Negative size"); + if (startAddress < 0) + throw new IllegalArgumentException("Negative start address"); + if ((startAddress + length) / 2 > mem.size()) + throw new IllegalArgumentException("End address higher than memory size"); if (length > Integer.MAX_VALUE) - throw new IllegalArgumentException("Can't create a memory block bigger than Integer.MAX_VALUE (" + Integer.MAX_VALUE + "\""); - if (startAddress % 2 != 0) - throw new IllegalArgumentException("Can't create an unaligned memory block"); + throw new IllegalArgumentException("Memory block bigger than Integer.MAX_VALUE (" + Integer.MAX_VALUE + "\""); + if (startAddress % 2 != 0 || length % 2 != 0) + throw new IllegalArgumentException("Unaligned memory block"); this.debugTarget = debugTarget; - this.mem = debugTarget.getMachine().getMainMemory(); + this.mem = mem; this.startAddress = startAddress; this.length = (int) length; + + mem.registerCellModifiedListener(e -> + { + if (e >= startAddress / 2 && e < (startAddress + length) / 2) + fireContentChangeEvent(); + }); } @Override @@ -48,13 +66,6 @@ public class MainMemoryBlock implements IMemoryBlock return debugTarget.getLaunch(); } - @Override - public T getAdapter(Class adapter) - { - // TODO - return null; - } - @Override public long getStartAddress() { @@ -70,13 +81,12 @@ public class MainMemoryBlock implements IMemoryBlock @Override public byte[] getBytes() throws DebugException { - // TODO speedup. Maybe make a method for this in MainMemory? byte[] bs = new byte[length]; int i; long j; - for (i = 0, j = startAddress / 2; i < length; i++) + for (i = 0, j = startAddress / 2; i < length; i += 2, j++) { - short word = mem.getCellAsBigInteger(j).shortValueExact(); + short word = mem.getCellAsBigInteger(j).shortValue(); bs[i + 0] = (byte) (word & 0xFF); bs[i + 1] = (byte) (word >>> 8); } @@ -86,13 +96,40 @@ public class MainMemoryBlock implements IMemoryBlock @Override public boolean supportsValueModification() { - // TODO - return false; + return true; } @Override public void setValue(long offset, byte[] bytes) throws DebugException { - // TODO + if (offset % 2 != 0 || bytes.length % 2 != 0) + throw new IllegalArgumentException("Can't write unaligned to a memory block"); + int i; + long j; + for (i = 0, j = (startAddress + offset) / 2; i < bytes.length; i += 2, j++) + { + short word = 0; + word |= bytes[i + 0] & 0xFF; + word |= bytes[i + 1] << 8; + mem.setCellAsBigInteger(j, BigInteger.valueOf(word)); + } + } + + /** + * Fires a terminate event for this debug element. + */ + private void fireContentChangeEvent() + { + fireEvent(new DebugEvent(this, DebugEvent.TERMINATE)); + } + + /** + * Fires a debug event. + * + * @param event debug event to fire + */ + private static void fireEvent(DebugEvent event) + { + DebugPlugin.getDefault().fireDebugEventSet(new DebugEvent[] { event }); } } \ No newline at end of file diff --git a/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MainMemoryBlockExtension.java b/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MainMemoryBlockExtension.java new file mode 100644 index 00000000..9355ae26 --- /dev/null +++ b/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MainMemoryBlockExtension.java @@ -0,0 +1,348 @@ +package net.mograsim.plugin.launch; + +import java.math.BigInteger; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.PlatformObject; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.DebugEvent; +import org.eclipse.debug.core.DebugException; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.model.IDebugTarget; +import org.eclipse.debug.core.model.IMemoryBlockExtension; +import org.eclipse.debug.core.model.IMemoryBlockRetrievalExtension; +import org.eclipse.debug.core.model.MemoryByte; + +import net.mograsim.machine.MainMemory; +import net.mograsim.machine.MainMemoryDefinition; +import net.mograsim.machine.Memory.MemoryCellModifiedListener; +import net.mograsim.plugin.MograsimActivator; + +public class MainMemoryBlockExtension extends PlatformObject implements IMemoryBlockExtension +{ + // TODO do we want to make the memory accessible byte-wise? + + private final String expression; + private final MachineDebugTarget debugTarget; + private final MainMemory mem; + + private final MainMemoryDefinition memDef; + private final int cellWidthBits; + private final int cellWidthBytes; + private final BigInteger cellWidthBytesBI; + private final BigInteger minAddrWords; + private final BigInteger maxAddrWords; + + private BigInteger baseAddrWords; + private BigInteger lengthWords; + + private final Set clients; + private final MemoryCellModifiedListener memListener; + private final AtomicBoolean memListenerRegistered; + + public MainMemoryBlockExtension(MachineDebugTarget debugTarget, String expression, @SuppressWarnings("unused") Object expressionContext) + throws DebugException + { + this.expression = expression; + this.debugTarget = debugTarget; + this.mem = debugTarget.getMachine().getMainMemory(); + + this.memDef = mem.getDefinition(); + this.cellWidthBits = memDef.getCellWidth(); + this.cellWidthBytes = (cellWidthBits + 7) / 8; + this.cellWidthBytesBI = BigInteger.valueOf(cellWidthBytes); + this.minAddrWords = BigInteger.valueOf(memDef.getMinimalAddress()); + this.maxAddrWords = BigInteger.valueOf(memDef.getMaximalAddress()); + + // TODO parse expression better + this.baseAddrWords = new BigInteger(expression, 16); + this.lengthWords = BigInteger.ONE; + + if (baseAddrWords.compareTo(minAddrWords) < 0 || baseAddrWords.compareTo(maxAddrWords) > 0) + throwDebugException("Base address out of range"); + if (baseAddrWords.add(lengthWords).compareTo(maxAddrWords) > 0) + throwDebugException("End address out of range"); + + this.clients = new HashSet<>(); + // don't check whether the address is in range, because this memory block could be read outside its "range" + this.memListener = a -> fireContentChangeEvent(); + this.memListenerRegistered = new AtomicBoolean(); + } + + @Override + public long getStartAddress() + { + return baseAddrWords.multiply(cellWidthBytesBI).longValueExact(); + } + + @Override + public long getLength() + { + return lengthWords.multiply(cellWidthBytesBI).longValueExact(); + } + + @Override + public byte[] getBytes() throws DebugException + { + BigInteger endAddrWords = baseAddrWords.add(lengthWords); + if (endAddrWords.compareTo(maxAddrWords) > 0) + throwDebugException("End address out of range"); + int lengthBytes = lengthWords.multiply(cellWidthBytesBI).intValueExact(); + + byte[] bytes = new byte[lengthBytes]; + int i; + long j; + for (i = 0, j = baseAddrWords.longValue(); i < lengthBytes; i += cellWidthBytes, j++) + { + BigInteger word = mem.getCellAsBigInteger(j); + System.arraycopy(word.toByteArray(), 0, bytes, i, cellWidthBytes); + } + return bytes; + } + + @Override + public boolean supportsValueModification() + { + return true; + } + + @Override + public void setValue(long offset, byte[] bytes) throws DebugException + { + if (offset % cellWidthBytes != 0 || bytes.length % cellWidthBytes != 0) + throwDebugException("Requested unaligned memory write"); + BigInteger startAddrWords = baseAddrWords.add(BigInteger.valueOf(offset / cellWidthBytes)); + if (startAddrWords.compareTo(minAddrWords) < 0 || startAddrWords.compareTo(maxAddrWords) > 0) + throwDebugException("Start address out of range"); + + BigInteger endAddrWords = startAddrWords.add(BigInteger.valueOf(bytes.length / cellWidthBytes)); + if (endAddrWords.compareTo(maxAddrWords) > 0) + throwDebugException("End address out of range"); + + int i; + long j; + for (i = 0, j = startAddrWords.longValue(); i < bytes.length; i += cellWidthBytes, j++) + { + BigInteger word = new BigInteger(bytes, i, cellWidthBytes); + mem.setCellAsBigInteger(j, word); + } + } + + @Override + public String getModelIdentifier() + { + return MograsimActivator.PLUGIN_ID; + } + + @Override + public IDebugTarget getDebugTarget() + { + return debugTarget; + } + + @Override + public ILaunch getLaunch() + { + return debugTarget.getLaunch(); + } + + @Override + public String getExpression() + { + return expression; + } + + @Override + public BigInteger getBigBaseAddress() throws DebugException + { + return baseAddrWords; + } + + @Override + public BigInteger getMemoryBlockStartAddress() throws DebugException + { + return minAddrWords; + } + + @Override + public BigInteger getMemoryBlockEndAddress() throws DebugException + { + return maxAddrWords; + } + + @Override + public BigInteger getBigLength() throws DebugException + { + return maxAddrWords.subtract(minAddrWords); + } + + @Override + public int getAddressSize() throws DebugException + { + return Long.BYTES; + } + + @Override + public boolean supportBaseAddressModification() throws DebugException + { + return true; + } + + @Override + public boolean supportsChangeManagement() + { + return false; + } + + @Override + public void setBaseAddress(BigInteger address) throws DebugException + { + if (address.compareTo(minAddrWords) < 0 || address.compareTo(maxAddrWords) > 0) + throwDebugException("Address out of range"); + this.baseAddrWords = address; + } + + @Override + public MemoryByte[] getBytesFromOffset(BigInteger unitOffset, long addressableUnits) throws DebugException + { + return getBytesFromAddress(getBigBaseAddress().add(unitOffset), addressableUnits); + } + + @Override + public MemoryByte[] getBytesFromAddress(BigInteger address, long units) throws DebugException + { + if (units < 0) + throwDebugException("Requested negative amount of unites"); + int lengthBytes = BigInteger.valueOf(units).multiply(cellWidthBytesBI).intValueExact(); + + MemoryByte[] bytes = new MemoryByte[lengthBytes]; + int i; + BigInteger j; + for (i = 0, j = address; i < lengthBytes; i += cellWidthBytes, j = j.add(BigInteger.ONE)) + { + if (j.compareTo(minAddrWords) >= 0 && j.compareTo(maxAddrWords) <= 0) + { + BigInteger word = mem.getCellAsBigInteger(j.longValue()); + byte[] wordBytes = word.toByteArray(); + int k; + for (k = 0; k < cellWidthBytes - wordBytes.length; k++) + bytes[i + k] = new MemoryByte(); + for (int l = 0; k < cellWidthBytes; k++) + bytes[i + k] = new MemoryByte(wordBytes[l]); + } else + for (int k = 0; k < cellWidthBytes; k++) + bytes[i + k] = new MemoryByte((byte) 0, (byte) 0); + } + return bytes; + } + + @Override + public void setValue(BigInteger offset, byte[] bytes) throws DebugException + { + if (bytes.length % cellWidthBytes != 0) + throwDebugException("Requested unaligned memory write"); + BigInteger startAddrWords = baseAddrWords.add(offset); + if (startAddrWords.compareTo(minAddrWords) < 0 || startAddrWords.compareTo(maxAddrWords) > 0) + throwDebugException("Start address out of range"); + BigInteger endAddrWords = startAddrWords.add(BigInteger.valueOf(bytes.length / cellWidthBytes)); + if (endAddrWords.compareTo(maxAddrWords) > 0) + throwDebugException("End address out of range"); + + unregisterMemoryListener(); + + int i; + long j; + for (i = 0, j = startAddrWords.longValue(); i < bytes.length; i += cellWidthBytes, j++) + { + BigInteger word = new BigInteger(bytes, i, cellWidthBytes); + mem.setCellAsBigInteger(j, word); + } + + if (!clients.isEmpty()) + registerMemoryListener(); + fireContentChangeEvent(); + } + + @Override + public void connect(Object client) + { + registerMemoryListener(); + clients.add(client); + } + + @Override + public void disconnect(Object client) + { + clients.remove(client); + + if (clients.isEmpty()) + unregisterMemoryListener(); + } + + @Override + public Object[] getConnections() + { + + Set clientsLocal = clients; + return clientsLocal == null ? new Object[0] : clientsLocal.toArray(); + } + + private void registerMemoryListener() + { + if (!memListenerRegistered.getAndSet(true)) + mem.registerCellModifiedListener(memListener); + } + + private void unregisterMemoryListener() + { + if (memListenerRegistered.getAndSet(false)) + mem.deregisterCellModifiedListener(memListener); + } + + @Override + public void dispose() throws DebugException + { + clients.clear(); + unregisterMemoryListener(); + } + + @Override + public IMemoryBlockRetrievalExtension getMemoryBlockRetrieval() + { + return debugTarget; + } + + @Override + public int getAddressableSize() throws DebugException + { + return cellWidthBytes; + } + + /** + * Fires a terminate event for this debug element. + */ + private void fireContentChangeEvent() + { + fireEvent(new DebugEvent(this, DebugEvent.CHANGE, DebugEvent.CONTENT)); + } + + /** + * Fires a debug event. + * + * @param event debug event to fire + */ + private static void fireEvent(DebugEvent event) + { + DebugPlugin.getDefault().fireDebugEventSet(new DebugEvent[] { event }); + } + + private static void throwDebugException(String message) throws DebugException + { + throw new DebugException( + new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, DebugException.TARGET_REQUEST_FAILED, message, null)); + } +} \ No newline at end of file -- 2.17.1