From a873ef940ba160f284ba6fa3fee1b9704bf68858 Mon Sep 17 00:00:00 2001 From: Daniel Kirschten Date: Sun, 29 Sep 2019 14:36:56 +0200 Subject: [PATCH] Further work on machine launch configs --- plugins/net.mograsim.plugin.core/plugin.xml | 2 +- .../plugin/launch/MachineDebugTarget.java | 283 ++++++++++++++++++ .../launch/MachineLaunchConfigType.java | 36 +-- .../plugin/launch/MachineProcess.java | 265 ++++++++++++++++ .../plugin/launch/MainMemoryBlock.java | 98 ++++++ 5 files changed, 666 insertions(+), 18 deletions(-) create mode 100644 plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineDebugTarget.java create mode 100644 plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineProcess.java create mode 100644 plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MainMemoryBlock.java diff --git a/plugins/net.mograsim.plugin.core/plugin.xml b/plugins/net.mograsim.plugin.core/plugin.xml index 0c1efa6a..1120682a 100644 --- a/plugins/net.mograsim.plugin.core/plugin.xml +++ b/plugins/net.mograsim.plugin.core/plugin.xml @@ -362,7 +362,7 @@ 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 new file mode 100644 index 00000000..5270c767 --- /dev/null +++ b/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineDebugTarget.java @@ -0,0 +1,283 @@ +package net.mograsim.plugin.launch; + +import java.util.Arrays; + +import org.eclipse.core.resources.IMarkerDelta; +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.ILaunchConfiguration; +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.IProcess; +import org.eclipse.debug.core.model.IStepFilters; +import org.eclipse.debug.core.model.IThread; + +import net.mograsim.machine.Machine; +import net.mograsim.plugin.MograsimActivator; + +public class MachineDebugTarget extends PlatformObject implements IDebugTarget +{ + private final MachineProcess process; + + public MachineDebugTarget(MachineProcess process) + { + this.process = process; + + DebugPlugin.getDefault().addDebugEventListener(es -> Arrays.stream(es).filter(e -> e.getSource() == process).forEach(e -> + { + switch (e.getKind()) + { + case DebugEvent.RESUME: + fireResumeEvent(e.getDetail()); + break; + case DebugEvent.SUSPEND: + fireSuspendEvent(e.getDetail()); + break; + case DebugEvent.TERMINATE: + fireTerminateEvent(); + break; + default: + // ignore + } + })); + + getLaunch().addDebugTarget(this); + fireCreationEvent(); + } + + public Machine getMachine() + { + return process.getMachine(); + } + + @Override + public String getName() throws DebugException + { + return process.getName(); + } + + @Override + public String getModelIdentifier() + { + return MograsimActivator.PLUGIN_ID; + } + + @Override + public IDebugTarget getDebugTarget() + { + return this; + } + + @Override + public ILaunch getLaunch() + { + return process.getLaunch(); + } + + public void setExecutionSpeed(double speed) + { + process.setExecutionSpeed(speed); + } + + @Override + public boolean isSuspended() + { + return process.isSuspended(); + } + + @Override + public boolean canSuspend() + { + return process.canSuspend(); + } + + @Override + public void suspend() throws DebugException + { + process.suspend(); + } + + @Override + public boolean canResume() + { + return process.canResume(); + } + + @Override + public void resume() throws DebugException + { + process.resume(); + } + + @Override + public boolean isTerminated() + { + return process.isTerminated(); + } + + @Override + public boolean canTerminate() + { + return process.canTerminate(); + } + + @Override + public void terminate() throws DebugException + { + process.terminate(); + } + + @Override + public boolean supportsBreakpoint(IBreakpoint breakpoint) + { + return false; + } + + @Override + public void breakpointAdded(IBreakpoint breakpoint) + { + // ignore; we don't support breakpoints + } + + @Override + public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta delta) + { + // ignore; we don't support breakpoints + } + + @Override + public void breakpointChanged(IBreakpoint breakpoint, IMarkerDelta delta) + { + // ignore; we don't support breakpoints + } + + @Override + public boolean isDisconnected() + { + return false; + } + + @Override + public boolean canDisconnect() + { + return false; + } + + @Override + public void disconnect() throws DebugException + { + throw new DebugException(new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, DebugException.NOT_SUPPORTED, + "Can't disconnect from a MachineDebugTarget", null)); + } + + @Override + public boolean supportsStorageRetrieval() + { + return true; + } + + @Override + public IMemoryBlock getMemoryBlock(long startAddress, long length) throws DebugException + { + return new MainMemoryBlock(this, startAddress, length); + } + + @Override + public MachineProcess getProcess() + { + return process; + } + + @Override + public boolean hasThreads() throws DebugException + { + return false; + } + + @Override + public IThread[] getThreads() throws DebugException + { + return new IThread[0]; + } + + @SuppressWarnings("unchecked") + @Override + public T getAdapter(Class adapter) + { + if (adapter == IDebugElement.class) + return (T) this; + + // leave this here; maybe we implement IStepFilters someday + if (adapter == IStepFilters.class) + if (this instanceof IStepFilters) + return (T) getDebugTarget(); + + if (adapter == IDebugTarget.class) + return (T) getDebugTarget(); + + if (adapter == ILaunch.class) + return (T) getLaunch(); + + if (adapter == IProcess.class) + return (T) getProcess(); + + // CONTEXTLAUNCHING + if (adapter == ILaunchConfiguration.class) + return (T) getLaunch().getLaunchConfiguration(); + + return super.getAdapter(adapter); + } + + /** + * Fires a creation event for this debug element. + */ + private void fireCreationEvent() + { + fireEvent(new DebugEvent(this, DebugEvent.CREATE)); + } + + /** + * Fires a resume for this debug element with the specified detail code. + * + * @param detail detail code for the resume event, such as DebugEvent.STEP_OVER + */ + private void fireResumeEvent(int detail) + { + fireEvent(new DebugEvent(this, DebugEvent.RESUME, detail)); + } + + /** + * Fires a suspend event for this debug element with the specified detail code. + * + * @param detail detail code for the suspend event, such as DebugEvent.BREAKPOINT + */ + private void fireSuspendEvent(int detail) + { + fireEvent(new DebugEvent(this, DebugEvent.SUSPEND, detail)); + } + + /** + * Fires a terminate event for this debug element. + */ + private void fireTerminateEvent() + { + 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/MachineLaunchConfigType.java b/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineLaunchConfigType.java index 03c481a5..27aca7e1 100644 --- a/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineLaunchConfigType.java +++ b/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineLaunchConfigType.java @@ -1,18 +1,19 @@ package net.mograsim.plugin.launch; -import java.io.IOException; - +import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResourceChangeEvent; import org.eclipse.core.resources.IResourceChangeListener; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.model.LaunchConfigurationDelegate; +import net.mograsim.machine.MachineDefinition; import net.mograsim.plugin.MograsimActivator; +import net.mograsim.plugin.nature.MachineContext; +import net.mograsim.plugin.nature.ProjectMachineContext; public class MachineLaunchConfigType extends LaunchConfigurationDelegate { @@ -23,25 +24,26 @@ public class MachineLaunchConfigType extends LaunchConfigurationDelegate public MachineLaunchConfigType() { this.resChangedListener = this::resourceChanged; + ResourcesPlugin.getWorkspace().addResourceChangeListener(resChangedListener, + // IResourceChangeEvent.POST_BUILD | + IResourceChangeEvent.POST_CHANGE | + // IResourceChangeEvent.PRE_BUILD | + // IResourceChangeEvent.PRE_CLOSE | + // IResourceChangeEvent.PRE_DELETE | + // IResourceChangeEvent.PRE_REFRESH | + 0); } @Override public void launch(ILaunchConfiguration configuration, String mode, ILaunch launch, IProgressMonitor monitor) throws CoreException { - ResourcesPlugin.getWorkspace().addResourceChangeListener(resChangedListener, - IResourceChangeEvent.POST_BUILD | IResourceChangeEvent.POST_CHANGE | IResourceChangeEvent.PRE_BUILD - | IResourceChangeEvent.PRE_CLOSE | IResourceChangeEvent.PRE_DELETE | IResourceChangeEvent.PRE_REFRESH); - System.out.println("launch"); - // TODO start a machine - ProcessBuilder pb = new ProcessBuilder("cmd", "/c", "\"echo Press Enter... &&pause>NUL && echo finished\""); - try - { - launch.addProcess(DebugPlugin.newProcess(launch, pb.start(), "")); - } - catch (IOException e) - { - e.printStackTrace(); - } + String projName = configuration.getAttribute(PROJECT_ATTR, ""); + IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projName); + MachineContext machineContext = ProjectMachineContext.getMachineContextOf(project); + MachineDefinition machineDefinition = machineContext.getMachineDefinition().orElseThrow(); + + MachineDebugTarget debugTarget = new MachineDebugTarget(new MachineProcess(launch, machineDefinition)); + debugTarget.setExecutionSpeed(10d); } private void resourceChanged(IResourceChangeEvent event) 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 new file mode 100644 index 00000000..b22d9c5c --- /dev/null +++ b/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineProcess.java @@ -0,0 +1,265 @@ +package net.mograsim.plugin.launch; + +import java.util.HashMap; +import java.util.Map; + +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.ILaunchConfiguration; +import org.eclipse.debug.core.model.IDebugTarget; +import org.eclipse.debug.core.model.IProcess; +import org.eclipse.debug.core.model.IStreamsProxy; +import org.eclipse.debug.core.model.ISuspendResume; + +import net.mograsim.logic.model.LogicExecuter; +import net.mograsim.machine.Machine; +import net.mograsim.machine.MachineDefinition; +import net.mograsim.plugin.MograsimActivator; + +public class MachineProcess extends PlatformObject implements IProcess, ISuspendResume +{ + private final ILaunch launch; + private final Machine machine; + private final LogicExecuter exec; + + private boolean running; + + public MachineProcess(ILaunch launch, MachineDefinition machineDefinition) + { + this.launch = launch; + this.machine = machineDefinition.createNew(); + this.exec = new LogicExecuter(machine.getTimeline()); + + exec.startLiveExecution(); + running = true; + + launch.addProcess(this); + fireCreationEvent(); + } + + public Machine getMachine() + { + return machine; + } + + @Override + public ILaunch getLaunch() + { + return launch; + } + + public String getName() + { + return "Mograsim machine \"" + machine.getDefinition().getId() + '"'; + } + + @Override + public String getLabel() + { + return getName() + " (" + getStatusString() + ')'; + } + + public String getStatusString() + { + return isTerminated() ? "terminated" : isSuspended() ? "paused" : "running"; + } + + public void setExecutionSpeed(double speed) + { + exec.setSpeedFactor(speed); + } + + @Override + public boolean isSuspended() + { + return exec.isPaused(); + } + + @Override + public boolean canSuspend() + { + return !isTerminated() && !isSuspended(); + } + + @Override + 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)); + if (isSuspended()) + throw new DebugException(new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, DebugException.TARGET_REQUEST_FAILED, + "Can't suspend a suspended MachineProcess", null)); + + exec.pauseLiveExecution(); + fireSuspendEvent(DebugEvent.CLIENT_REQUEST); + } + + @Override + public boolean canResume() + { + return !isTerminated() && isSuspended(); + } + + @Override + 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)); + if (!isSuspended()) + throw new DebugException(new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, DebugException.TARGET_REQUEST_FAILED, + "Can't resume a non-suspended MachineProcess", null)); + + exec.unpauseLiveExecution(); + fireResumeEvent(DebugEvent.CLIENT_REQUEST); + } + + @Override + public boolean isTerminated() + { + return !running; + } + + @Override + public boolean canTerminate() + { + return !isTerminated(); + } + + @Override + public void terminate() throws DebugException + { + if (isTerminated()) + return; + + exec.stopLiveExecution(); + running = false; + fireTerminateEvent(); + } + + @Override + 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)); + + return 0; + } + + @Override + public IStreamsProxy getStreamsProxy() + { + return null; + } + + private Map attributes; + + @Override + public void setAttribute(String key, String value) + { + if (attributes == null) + attributes = new HashMap<>(5); + Object origVal = attributes.get(key); + if (origVal != null && origVal.equals(value)) + return; + attributes.put(key, value); + fireChangeEvent(); + } + + @Override + public String getAttribute(String key) + { + if (attributes == null) + return null; + return attributes.get(key); + } + + @SuppressWarnings("unchecked") + @Override + public T getAdapter(Class adapter) + { + if (adapter.equals(IProcess.class)) + return (T) this; + + if (adapter.equals(IDebugTarget.class)) + { + ILaunch launch = getLaunch(); + IDebugTarget[] targets = launch.getDebugTargets(); + for (int i = 0; i < targets.length; i++) + if (this.equals(targets[i].getProcess())) + return (T) targets[i]; + return null; + } + + if (adapter.equals(ILaunch.class)) + return (T) getLaunch(); + + if (adapter.equals(ILaunchConfiguration.class)) + return (T) getLaunch().getLaunchConfiguration(); + + return super.getAdapter(adapter); + } + + /** + * Fires a creation event. + */ + private void fireCreationEvent() + { + fireEvent(new DebugEvent(this, DebugEvent.CREATE)); + } + + /** + * Fires a change event. + */ + private void fireChangeEvent() + { + fireEvent(new DebugEvent(this, DebugEvent.CHANGE)); + } + + /** + * Fires a resume for this debug element with the specified detail code. + * + * @param detail detail code for the resume event, such as DebugEvent.STEP_OVER + */ + private void fireResumeEvent(int detail) + { + fireEvent(new DebugEvent(this, DebugEvent.RESUME, detail)); + } + + /** + * Fires a suspend event for this debug element with the specified detail code. + * + * @param detail detail code for the suspend event, such as DebugEvent.BREAKPOINT + */ + private void fireSuspendEvent(int detail) + { + fireEvent(new DebugEvent(this, DebugEvent.SUSPEND, detail)); + } + + /** + * Fires a terminate event. + */ + private void fireTerminateEvent() + { + fireEvent(new DebugEvent(this, DebugEvent.TERMINATE)); + } + + /** + * Fires the given debug event. + * + * @param event debug event to fire + */ + private static void fireEvent(DebugEvent event) + { + DebugPlugin manager = DebugPlugin.getDefault(); + if (manager != null) + manager.fireDebugEventSet(new DebugEvent[] { event }); + } +} \ 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 new file mode 100644 index 00000000..b068fc09 --- /dev/null +++ b/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MainMemoryBlock.java @@ -0,0 +1,98 @@ +package net.mograsim.plugin.launch; + +import org.eclipse.debug.core.DebugException; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.model.IDebugTarget; +import org.eclipse.debug.core.model.IMemoryBlock; + +import net.mograsim.machine.MainMemory; +import net.mograsim.plugin.MograsimActivator; + +public class MainMemoryBlock implements IMemoryBlock +{ + private final MachineDebugTarget debugTarget; + private final MainMemory mem; + private final long startAddress; + private final int length; + + public MainMemoryBlock(MachineDebugTarget debugTarget, long startAddress, long length) + { + if (length < 0) + throw new IllegalArgumentException("Can't create a memory block of negative 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"); + + this.debugTarget = debugTarget; + this.mem = debugTarget.getMachine().getMainMemory(); + this.startAddress = startAddress; + this.length = (int) length; + } + + @Override + public String getModelIdentifier() + { + return MograsimActivator.PLUGIN_ID; + } + + @Override + public IDebugTarget getDebugTarget() + { + return debugTarget; + } + + @Override + public ILaunch getLaunch() + { + return debugTarget.getLaunch(); + } + + @Override + public T getAdapter(Class adapter) + { + // TODO + return null; + } + + @Override + public long getStartAddress() + { + return startAddress; + } + + @Override + public long getLength() + { + return length; + } + + @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++) + { + short word = mem.getCellAsBigInteger(j).shortValueExact(); + bs[i + 0] = (byte) (word & 0xFF); + bs[i + 1] = (byte) (word >>> 8); + } + return bs; + } + + @Override + public boolean supportsValueModification() + { + // TODO + return false; + } + + @Override + public void setValue(long offset, byte[] bytes) throws DebugException + { + // TODO + } +} \ No newline at end of file -- 2.17.1