From 79fe4ef5f67bdf3b7a9d8002d1759ce87b3f90be Mon Sep 17 00:00:00 2001 From: Daniel Kirschten Date: Wed, 2 Oct 2019 18:17:34 +0200 Subject: [PATCH] Added threads, stackframes, and registers to the debug model --- .../plugin/launch/MachineDebugTarget.java | 10 +- .../plugin/launch/MachineRegister.java | 166 +++++++++++++ .../plugin/launch/MachineRegisterGroup.java | 74 ++++++ .../plugin/launch/MachineStackFrame.java | 193 ++++++++++++++ .../mograsim/plugin/launch/MachineThread.java | 235 ++++++++++++++++++ .../mograsim/plugin/launch/MachineValue.java | 74 ++++++ .../mograsim/plugin/views/SimulationView.java | 21 +- 7 files changed, 759 insertions(+), 14 deletions(-) create mode 100644 plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineRegister.java create mode 100644 plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineRegisterGroup.java create mode 100644 plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineStackFrame.java create mode 100644 plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineThread.java create mode 100644 plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineValue.java 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 5a134017..e1efb553 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 @@ -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 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 index 00000000..8c0685dc --- /dev/null +++ b/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineRegister.java @@ -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 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 index 00000000..081ec41f --- /dev/null +++ b/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineRegisterGroup.java @@ -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 registers; + + public MachineRegisterGroup(MachineStackFrame stackFrame) + { + this.stackFrame = stackFrame; + Set machineRegisters = getMachine().getDefinition().getRegisters(); + List 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 index 00000000..14f64206 --- /dev/null +++ b/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineStackFrame.java @@ -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 index 00000000..4249bb69 --- /dev/null +++ b/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineThread.java @@ -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 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 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 index 00000000..02b84b9e --- /dev/null +++ b/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineValue.java @@ -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 diff --git a/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/views/SimulationView.java b/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/views/SimulationView.java index 4d118a9f..12126616 100644 --- a/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/views/SimulationView.java +++ b/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/views/SimulationView.java @@ -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; } } -- 2.17.1