Added threads, stackframes, and registers to the debug model
authorDaniel Kirschten <daniel.kirschten@gmx.de>
Wed, 2 Oct 2019 16:17:34 +0000 (18:17 +0200)
committerDaniel Kirschten <daniel.kirschten@gmx.de>
Wed, 2 Oct 2019 16:17:34 +0000 (18:17 +0200)
plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineDebugTarget.java
plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineRegister.java [new file with mode: 0644]
plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineRegisterGroup.java [new file with mode: 0644]
plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineStackFrame.java [new file with mode: 0644]
plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineThread.java [new file with mode: 0644]
plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineValue.java [new file with mode: 0644]
plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/views/SimulationView.java

index 5a13401..e1efb55 100644 (file)
@@ -30,9 +30,12 @@ import net.mograsim.plugin.MograsimActivator;
 
 public class MachineDebugTarget extends PlatformObject implements IDebugTarget, IMemoryBlockRetrievalExtension
 {
+       private final static boolean USE_PSEUDO_THREAD = true;
+
        private final ILaunch launch;
        private final Machine machine;
        private final LogicExecuter exec;
+       private final MachineThread thread;
 
        private boolean running;
 
@@ -51,6 +54,9 @@ public class MachineDebugTarget extends PlatformObject implements IDebugTarget,
 
                getLaunch().addDebugTarget(this);
                fireCreationEvent();
+
+               // create after creating ourself
+               this.thread = USE_PSEUDO_THREAD ? new MachineThread(this) : null;
        }
 
        public Machine getMachine()
@@ -232,13 +238,13 @@ public class MachineDebugTarget extends PlatformObject implements IDebugTarget,
        @Override
        public boolean hasThreads() throws DebugException
        {
-               return false;
+               return USE_PSEUDO_THREAD;
        }
 
        @Override
        public IThread[] getThreads() throws DebugException
        {
-               return new IThread[0];
+               return thread == null ? new IThread[0] : new IThread[] { thread };
        }
 
        public void addExecutionSpeedListener(Consumer<Double> executionSpeedListener)
diff --git a/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineRegister.java b/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineRegister.java
new file mode 100644 (file)
index 0000000..8c0685d
--- /dev/null
@@ -0,0 +1,166 @@
+package net.mograsim.plugin.launch;
+
+import java.util.Arrays;
+import java.util.function.Consumer;
+
+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.IDebugElement;
+import org.eclipse.debug.core.model.IDebugTarget;
+import org.eclipse.debug.core.model.IRegister;
+import org.eclipse.debug.core.model.IRegisterGroup;
+import org.eclipse.debug.core.model.IValue;
+import org.eclipse.swt.SWT;
+
+import net.mograsim.logic.core.types.BitVector;
+import net.mograsim.machine.Machine;
+import net.mograsim.machine.Register;
+import net.mograsim.plugin.MograsimActivator;
+
+public class MachineRegister extends PlatformObject implements IRegister
+{
+       private final MachineRegisterGroup registerGroup;
+       private final Register machineRegister;
+
+       private final Consumer<BitVector> registerListener;
+
+       public MachineRegister(MachineRegisterGroup registerGroup, Register machineRegister)
+       {
+               this.registerGroup = registerGroup;
+               this.machineRegister = machineRegister;
+
+               this.registerListener = v -> fireChangeEvent();
+               getMachine().addRegisterListener(machineRegister, registerListener);
+
+               DebugPlugin.getDefault().addDebugEventListener(es -> Arrays.stream(es).filter(e -> e.getKind() == DebugEvent.TERMINATE).filter(e ->
+               {
+                       Object source = e.getSource();
+                       if (!(source instanceof IDebugElement))
+                               return false;
+                       return ((IDebugElement) source).getDebugTarget() == getDebugTarget();
+               }).forEach(e -> getMachine().removeRegisterListener(machineRegister, registerListener)));
+       }
+
+       public Machine getMachine()
+       {
+               return registerGroup.getMachine();
+       }
+
+       @Override
+       public IValue getValue() throws DebugException
+       {
+               return new MachineValue(this);
+       }
+
+       @Override
+       public String getName() throws DebugException
+       {
+               return machineRegister.id();// TODO name
+       }
+
+       @Override
+       public String getReferenceTypeName() throws DebugException
+       {
+               return "BitVector";
+       }
+
+       @Override
+       public boolean hasValueChanged() throws DebugException
+       {
+               // TODO
+               return false;
+       }
+
+       @Override
+       public String getModelIdentifier()
+       {
+               return MograsimActivator.PLUGIN_ID;
+       }
+
+       @Override
+       public IDebugTarget getDebugTarget()
+       {
+               return registerGroup.getDebugTarget();
+       }
+
+       @Override
+       public ILaunch getLaunch()
+       {
+               return registerGroup.getLaunch();
+       }
+
+       public String getValueString()
+       {
+               return getMachine().getRegister(machineRegister).toString();
+       }
+
+       @Override
+       public void setValue(String expression) throws DebugException
+       {
+               // TODO exception handling
+               getMachine().setRegister(machineRegister, BitVector.parse(expression));
+       }
+
+       @Override
+       public void setValue(IValue value) throws DebugException
+       {
+               if (!"Bitvector".equals(value.getReferenceTypeName()))
+                       throw new DebugException(new Status(SWT.ERROR, MograsimActivator.PLUGIN_ID, ""));
+               setValue(value.getValueString());
+       }
+
+       @Override
+       public boolean supportsValueModification()
+       {
+               return true;
+       }
+
+       @Override
+       public boolean verifyValue(String expression) throws DebugException
+       {
+               // TODO do this prettier; also check length too
+               try
+               {
+                       BitVector.parse(expression);
+               }
+               catch (@SuppressWarnings("unused") Exception e)
+               {
+                       return false;
+               }
+               return true;
+       }
+
+       @Override
+       public boolean verifyValue(IValue value) throws DebugException
+       {
+               return verifyValue(value.getValueString());
+       }
+
+       @Override
+       public IRegisterGroup getRegisterGroup() throws DebugException
+       {
+               return registerGroup;
+       }
+
+       /**
+        * Fires a change event for this debug element.
+        */
+       private void fireChangeEvent()
+       {
+               fireEvent(new DebugEvent(this, DebugEvent.CHANGE));
+       }
+
+       /**
+        * 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/MachineRegisterGroup.java b/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineRegisterGroup.java
new file mode 100644 (file)
index 0000000..081ec41
--- /dev/null
@@ -0,0 +1,74 @@
+package net.mograsim.plugin.launch;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.eclipse.core.runtime.PlatformObject;
+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.IRegister;
+import org.eclipse.debug.core.model.IRegisterGroup;
+
+import net.mograsim.machine.Machine;
+import net.mograsim.machine.Register;
+import net.mograsim.plugin.MograsimActivator;
+
+public class MachineRegisterGroup extends PlatformObject implements IRegisterGroup
+{
+       private final MachineStackFrame stackFrame;
+       private final List<MachineRegister> registers;
+
+       public MachineRegisterGroup(MachineStackFrame stackFrame)
+       {
+               this.stackFrame = stackFrame;
+               Set<Register> machineRegisters = getMachine().getDefinition().getRegisters();
+               List<MachineRegister> registersModifiable = machineRegisters.stream().map(r -> new MachineRegister(this, r))
+                               .collect(Collectors.toList());
+               this.registers = Collections.unmodifiableList(registersModifiable);
+       }
+
+       @Override
+       public String getModelIdentifier()
+       {
+               return MograsimActivator.PLUGIN_ID;
+       }
+
+       public Machine getMachine()
+       {
+               return stackFrame.getMachine();
+       }
+
+       @Override
+       public IDebugTarget getDebugTarget()
+       {
+               return stackFrame.getDebugTarget();
+       }
+
+       @Override
+       public ILaunch getLaunch()
+       {
+               return stackFrame.getLaunch();
+       }
+
+       @Override
+       public String getName() throws DebugException
+       {
+               return "pseudo register group";
+       }
+
+       @Override
+       public IRegister[] getRegisters() throws DebugException
+       {
+               // TODO sort
+               return registers.toArray(IRegister[]::new);
+       }
+
+       @Override
+       public boolean hasRegisters() throws DebugException
+       {
+               return true;
+       }
+}
\ No newline at end of file
diff --git a/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineStackFrame.java b/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineStackFrame.java
new file mode 100644 (file)
index 0000000..14f6420
--- /dev/null
@@ -0,0 +1,193 @@
+package net.mograsim.plugin.launch;
+
+import org.eclipse.core.runtime.PlatformObject;
+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.IRegisterGroup;
+import org.eclipse.debug.core.model.IStackFrame;
+import org.eclipse.debug.core.model.IThread;
+import org.eclipse.debug.core.model.IVariable;
+
+import net.mograsim.machine.Machine;
+import net.mograsim.plugin.MograsimActivator;
+
+public class MachineStackFrame extends PlatformObject implements IStackFrame
+{
+       private final MachineThread thread;
+       private final MachineRegisterGroup registerGroup;
+
+       public MachineStackFrame(MachineThread thread)
+       {
+               this.thread = thread;
+               this.registerGroup = new MachineRegisterGroup(this);
+       }
+
+       @Override
+       public String getModelIdentifier()
+       {
+               return MograsimActivator.PLUGIN_ID;
+       }
+
+       public Machine getMachine()
+       {
+               return thread.getMachine();
+       }
+
+       @Override
+       public IDebugTarget getDebugTarget()
+       {
+               return thread.getDebugTarget();
+       }
+
+       @Override
+       public ILaunch getLaunch()
+       {
+               return thread.getLaunch();
+       }
+
+       @Override
+       public boolean canStepInto()
+       {
+               return thread.canStepInto();
+       }
+
+       @Override
+       public boolean canStepOver()
+       {
+               return thread.canStepOver();
+       }
+
+       @Override
+       public boolean canStepReturn()
+       {
+               return thread.canStepReturn();
+       }
+
+       @Override
+       public boolean isStepping()
+       {
+               return thread.isStepping();
+       }
+
+       @Override
+       public void stepInto() throws DebugException
+       {
+               thread.stepInto();
+       }
+
+       @Override
+       public void stepOver() throws DebugException
+       {
+               thread.stepOver();
+       }
+
+       @Override
+       public void stepReturn() throws DebugException
+       {
+               thread.stepReturn();
+       }
+
+       @Override
+       public boolean canResume()
+       {
+               return thread.canResume();
+       }
+
+       @Override
+       public boolean canSuspend()
+       {
+               return thread.canSuspend();
+       }
+
+       @Override
+       public boolean isSuspended()
+       {
+               return thread.isSuspended();
+       }
+
+       @Override
+       public void resume() throws DebugException
+       {
+               thread.resume();
+       }
+
+       @Override
+       public void suspend() throws DebugException
+       {
+               thread.suspend();
+       }
+
+       @Override
+       public boolean canTerminate()
+       {
+               return thread.canTerminate();
+       }
+
+       @Override
+       public boolean isTerminated()
+       {
+               return thread.isTerminated();
+       }
+
+       @Override
+       public void terminate() throws DebugException
+       {
+               thread.terminate();
+       }
+
+       @Override
+       public IThread getThread()
+       {
+               return thread;
+       }
+
+       @Override
+       public IVariable[] getVariables() throws DebugException
+       {
+               return new IVariable[0];
+       }
+
+       @Override
+       public boolean hasVariables() throws DebugException
+       {
+               return false;
+       }
+
+       @Override
+       public int getLineNumber() throws DebugException
+       {
+               // TODO could we transmit the active microinstruction address using this?
+               return -1;
+       }
+
+       @Override
+       public int getCharStart() throws DebugException
+       {
+               return -1;
+       }
+
+       @Override
+       public int getCharEnd() throws DebugException
+       {
+               return -1;
+       }
+
+       @Override
+       public String getName() throws DebugException
+       {
+               return "pseudo stack frame";
+       }
+
+       @Override
+       public IRegisterGroup[] getRegisterGroups() throws DebugException
+       {
+               return new IRegisterGroup[] { registerGroup };
+       }
+
+       @Override
+       public boolean hasRegisterGroups() throws DebugException
+       {
+               return true;
+       }
+}
\ No newline at end of file
diff --git a/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineThread.java b/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineThread.java
new file mode 100644 (file)
index 0000000..4249bb6
--- /dev/null
@@ -0,0 +1,235 @@
+package net.mograsim.plugin.launch;
+
+import java.util.Arrays;
+
+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.IBreakpoint;
+import org.eclipse.debug.core.model.IDebugTarget;
+import org.eclipse.debug.core.model.IStackFrame;
+import org.eclipse.debug.core.model.IThread;
+
+import net.mograsim.machine.Machine;
+import net.mograsim.plugin.MograsimActivator;
+
+public class MachineThread extends PlatformObject implements IThread
+{
+       private final MachineDebugTarget debugTarget;
+       private final MachineStackFrame stackFrame;
+
+       public MachineThread(MachineDebugTarget debugTarget)
+       {
+               this.debugTarget = debugTarget;
+               this.stackFrame = new MachineStackFrame(this);
+
+               DebugPlugin.getDefault().addDebugEventListener(es -> Arrays.stream(es).filter(e -> e.getSource() == debugTarget).forEach(e ->
+               {
+                       switch (e.getKind())
+                       {
+                       case DebugEvent.RESUME:
+                               fireResumeEvent(e.getDetail());
+                               break;
+                       case DebugEvent.SUSPEND:
+                               fireSuspendEvent(e.getDetail());
+                               break;
+                       default:
+                       }
+               }));
+
+               fireCreationEvent();
+       }
+
+       @Override
+       public String getModelIdentifier()
+       {
+               return MograsimActivator.PLUGIN_ID;
+       }
+
+       public Machine getMachine()
+       {
+               return debugTarget.getMachine();
+       }
+
+       @Override
+       public IDebugTarget getDebugTarget()
+       {
+               return debugTarget;
+       }
+
+       @Override
+       public ILaunch getLaunch()
+       {
+               return debugTarget.getLaunch();
+       }
+
+       @Override
+       public boolean canResume()
+       {
+               return debugTarget.canResume();
+       }
+
+       @Override
+       public boolean canSuspend()
+       {
+               return debugTarget.canSuspend();
+       }
+
+       @Override
+       public boolean isSuspended()
+       {
+               return debugTarget.isSuspended();
+       }
+
+       @Override
+       public void resume() throws DebugException
+       {
+               debugTarget.resume();
+       }
+
+       @Override
+       public void suspend() throws DebugException
+       {
+               debugTarget.suspend();
+       }
+
+       @Override
+       public boolean canStepInto()
+       {
+               return false;
+       }
+
+       // TODO step over sounds like single-step execution...
+       @Override
+       public boolean canStepOver()
+       {
+               return false;
+       }
+
+       @Override
+       public boolean canStepReturn()
+       {
+               return false;
+       }
+
+       @Override
+       public boolean isStepping()
+       {
+               return false;
+       }
+
+       @Override
+       public void stepInto() throws DebugException
+       {
+               throw new DebugException(new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, "Can't step into for a machine thread"));
+       }
+
+       @Override
+       public void stepOver() throws DebugException
+       {
+               throw new DebugException(new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, "Can't step over for a machine thread"));
+       }
+
+       @Override
+       public void stepReturn() throws DebugException
+       {
+               throw new DebugException(new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, "Can't step return for a machine thread"));
+       }
+
+       @Override
+       public boolean canTerminate()
+       {
+               return debugTarget.canTerminate();
+       }
+
+       @Override
+       public boolean isTerminated()
+       {
+               return debugTarget.isTerminated();
+       }
+
+       @Override
+       public void terminate() throws DebugException
+       {
+               debugTarget.terminate();
+       }
+
+       @Override
+       public IStackFrame[] getStackFrames() throws DebugException
+       {
+               return isSuspended() ? new IStackFrame[] { stackFrame } : new IStackFrame[0];
+       }
+
+       @Override
+       public boolean hasStackFrames() throws DebugException
+       {
+               // required by Eclipse: see javadoc for org.eclipse.debug.core.DebugEvent
+               return isSuspended();
+       }
+
+       @Override
+       public int getPriority() throws DebugException
+       {
+               return 0;
+       }
+
+       @Override
+       public IStackFrame getTopStackFrame() throws DebugException
+       {
+               return stackFrame;
+       }
+
+       @Override
+       public String getName() throws DebugException
+       {
+               return "pseudo thread";
+       }
+
+       @Override
+       public IBreakpoint[] getBreakpoints()
+       {
+               return new IBreakpoint[0];
+       }
+
+       /**
+        * 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 <code>DebugEvent.STEP_OVER</code>
+        */
+       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 <code>DebugEvent.BREAKPOINT</code>
+        */
+       private void fireSuspendEvent(int detail)
+       {
+               fireEvent(new DebugEvent(this, DebugEvent.SUSPEND, detail));
+       }
+
+       /**
+        * 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/MachineValue.java b/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineValue.java
new file mode 100644 (file)
index 0000000..02b84b9
--- /dev/null
@@ -0,0 +1,74 @@
+package net.mograsim.plugin.launch;
+
+import org.eclipse.core.runtime.PlatformObject;
+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.IValue;
+import org.eclipse.debug.core.model.IVariable;
+
+import net.mograsim.machine.Machine;
+import net.mograsim.plugin.MograsimActivator;
+
+public class MachineValue extends PlatformObject implements IValue
+{
+       private final MachineRegister register;
+
+       public MachineValue(MachineRegister register)
+       {
+               this.register = register;
+       }
+
+       @Override
+       public String getModelIdentifier()
+       {
+               return MograsimActivator.PLUGIN_ID;
+       }
+
+       public Machine getMachine()
+       {
+               return register.getMachine();
+       }
+
+       @Override
+       public IDebugTarget getDebugTarget()
+       {
+               return register.getDebugTarget();
+       }
+
+       @Override
+       public ILaunch getLaunch()
+       {
+               return register.getLaunch();
+       }
+
+       @Override
+       public String getReferenceTypeName() throws DebugException
+       {
+               return register.getReferenceTypeName();
+       }
+
+       @Override
+       public String getValueString() throws DebugException
+       {
+               return register.getValueString();
+       }
+
+       @Override
+       public boolean isAllocated() throws DebugException
+       {
+               return false;
+       }
+
+       @Override
+       public IVariable[] getVariables() throws DebugException
+       {
+               return new IVariable[0];
+       }
+
+       @Override
+       public boolean hasVariables() throws DebugException
+       {
+               return false;
+       }
+}
\ No newline at end of file
index 4d118a9..1212661 100644 (file)
@@ -6,6 +6,7 @@ import java.util.function.Consumer;
 
 import org.eclipse.core.runtime.SafeRunner;
 import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.model.IDebugElement;
 import org.eclipse.debug.core.model.IDebugTarget;
 import org.eclipse.debug.ui.DebugUITools;
 import org.eclipse.debug.ui.contexts.IDebugContextListener;
@@ -183,24 +184,20 @@ public class SimulationView extends ViewPart
                        Object[] selectedElements = treeSelection.toArray();
                        for (Object selectedElement : selectedElements)
                        {
-                               MachineDebugTarget debugTarget;
-                               if (selectedElement instanceof MachineDebugTarget)
-                                       debugTarget = (MachineDebugTarget) selectedElement;
+                               IDebugTarget debugTarget;
+                               if (selectedElement instanceof IDebugElement)
+                                       debugTarget = ((IDebugElement) selectedElement).getDebugTarget();
                                else if (selectedElement instanceof ILaunch)
-                               {
-                                       ILaunch launch = (ILaunch) selectedElement;
-                                       IDebugTarget genericDebugTarget = launch.getDebugTarget();
-                                       if (genericDebugTarget instanceof MachineDebugTarget)
-                                               debugTarget = (MachineDebugTarget) genericDebugTarget;
-                                       else
-                                               continue;
-                               } else
+                                       debugTarget = ((ILaunch) selectedElement).getDebugTarget();
+                               else
+                                       continue;
+                               if (!(debugTarget instanceof MachineDebugTarget))
                                        continue;
                                if (debugTarget.isTerminated())
                                        continue;
                                // we found a selected MachineDebugTarget
                                if (this.debugTarget != debugTarget)
-                                       bindToDebugTarget(debugTarget);
+                                       bindToDebugTarget((MachineDebugTarget) debugTarget);
                                return;
                        }
                }