Added threads, stackframes, and registers to the debug model
[Mograsim.git] / plugins / net.mograsim.plugin.core / src / net / mograsim / plugin / launch / MachineDebugTarget.java
1 package net.mograsim.plugin.launch;
2
3 import java.util.ArrayList;
4 import java.util.List;
5 import java.util.function.Consumer;
6
7 import org.eclipse.core.resources.IMarkerDelta;
8 import org.eclipse.core.runtime.IStatus;
9 import org.eclipse.core.runtime.PlatformObject;
10 import org.eclipse.core.runtime.Status;
11 import org.eclipse.debug.core.DebugEvent;
12 import org.eclipse.debug.core.DebugException;
13 import org.eclipse.debug.core.DebugPlugin;
14 import org.eclipse.debug.core.ILaunch;
15 import org.eclipse.debug.core.ILaunchConfiguration;
16 import org.eclipse.debug.core.model.IBreakpoint;
17 import org.eclipse.debug.core.model.IDebugElement;
18 import org.eclipse.debug.core.model.IDebugTarget;
19 import org.eclipse.debug.core.model.IMemoryBlock;
20 import org.eclipse.debug.core.model.IMemoryBlockExtension;
21 import org.eclipse.debug.core.model.IMemoryBlockRetrievalExtension;
22 import org.eclipse.debug.core.model.IProcess;
23 import org.eclipse.debug.core.model.IStepFilters;
24 import org.eclipse.debug.core.model.IThread;
25
26 import net.mograsim.logic.model.LogicExecuter;
27 import net.mograsim.machine.Machine;
28 import net.mograsim.machine.MachineDefinition;
29 import net.mograsim.plugin.MograsimActivator;
30
31 public class MachineDebugTarget extends PlatformObject implements IDebugTarget, IMemoryBlockRetrievalExtension
32 {
33         private final static boolean USE_PSEUDO_THREAD = true;
34
35         private final ILaunch launch;
36         private final Machine machine;
37         private final LogicExecuter exec;
38         private final MachineThread thread;
39
40         private boolean running;
41
42         private final List<Consumer<Double>> executionSpeedListeners;
43
44         public MachineDebugTarget(ILaunch launch, MachineDefinition machineDefinition)
45         {
46                 this.launch = launch;
47                 this.machine = machineDefinition.createNew();
48                 this.exec = new LogicExecuter(machine.getTimeline());
49
50                 this.executionSpeedListeners = new ArrayList<>();
51
52                 exec.startLiveExecution();
53                 running = true;
54
55                 getLaunch().addDebugTarget(this);
56                 fireCreationEvent();
57
58                 // create after creating ourself
59                 this.thread = USE_PSEUDO_THREAD ? new MachineThread(this) : null;
60         }
61
62         public Machine getMachine()
63         {
64                 return machine;
65         }
66
67         @Override
68         public String getName() throws DebugException
69         {
70                 return "Mograsim machine \"" + machine.getDefinition().getId() + '"';
71         }
72
73         @Override
74         public String getModelIdentifier()
75         {
76                 return MograsimActivator.PLUGIN_ID;
77         }
78
79         @Override
80         public IDebugTarget getDebugTarget()
81         {
82                 return this;
83         }
84
85         @Override
86         public ILaunch getLaunch()
87         {
88                 return launch;
89         }
90
91         public double getExecutionSpeed()
92         {
93                 return exec.getSpeedFactor();
94         }
95
96         public void setExecutionSpeed(double speed)
97         {
98                 if (getExecutionSpeed() != speed)
99                 {
100                         exec.setSpeedFactor(speed);
101                         callExecutionSpeedListener(speed);
102                 }
103         }
104
105         @Override
106         public boolean isSuspended()
107         {
108                 return exec.isPaused();
109         }
110
111         @Override
112         public boolean canSuspend()
113         {
114                 return !isTerminated() && !isSuspended();
115         }
116
117         @Override
118         public void suspend() throws DebugException
119         {
120                 if (isTerminated())
121                         throwDebugException("Can't suspend a terminated MachineProcess");
122                 if (isSuspended())
123                         throwDebugException("Can't suspend a suspended MachineProcess");
124
125                 exec.pauseLiveExecution();
126                 fireSuspendEvent(DebugEvent.CLIENT_REQUEST);
127         }
128
129         @Override
130         public boolean canResume()
131         {
132                 return !isTerminated() && isSuspended();
133         }
134
135         @Override
136         public void resume() throws DebugException
137         {
138                 if (isTerminated())
139                         throwDebugException("Can't resume a terminated MachineProcess");
140                 if (!isSuspended())
141                         throwDebugException("Can't resume a non-suspended MachineProcess");
142
143                 exec.unpauseLiveExecution();
144                 fireResumeEvent(DebugEvent.CLIENT_REQUEST);
145         }
146
147         @Override
148         public boolean isTerminated()
149         {
150                 return !running;
151         }
152
153         @Override
154         public boolean canTerminate()
155         {
156                 return !isTerminated();
157         }
158
159         @Override
160         public void terminate() throws DebugException
161         {
162                 if (isTerminated())
163                         return;
164
165                 exec.stopLiveExecution();
166                 running = false;
167                 fireTerminateEvent();
168         }
169
170         @Override
171         public boolean supportsBreakpoint(IBreakpoint breakpoint)
172         {
173                 return false;
174         }
175
176         @Override
177         public void breakpointAdded(IBreakpoint breakpoint)
178         {
179                 // ignore; we don't support breakpoints
180         }
181
182         @Override
183         public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta delta)
184         {
185                 // ignore; we don't support breakpoints
186         }
187
188         @Override
189         public void breakpointChanged(IBreakpoint breakpoint, IMarkerDelta delta)
190         {
191                 // ignore; we don't support breakpoints
192         }
193
194         @Override
195         public boolean isDisconnected()
196         {
197                 return false;
198         }
199
200         @Override
201         public boolean canDisconnect()
202         {
203                 return false;
204         }
205
206         @Override
207         public void disconnect() throws DebugException
208         {
209                 throw new DebugException(new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, DebugException.NOT_SUPPORTED,
210                                 "Can't disconnect from a MachineDebugTarget", null));
211         }
212
213         @Override
214         public boolean supportsStorageRetrieval()
215         {
216                 return true;
217         }
218
219         @SuppressWarnings("deprecation") // TODO can we throw a DebugException instead?
220         @Override
221         public IMemoryBlock getMemoryBlock(long startAddress, long length) throws DebugException
222         {
223                 return new MainMemoryBlock(this, startAddress, length);
224         }
225
226         @Override
227         public IMemoryBlockExtension getExtendedMemoryBlock(String expression, Object context) throws DebugException
228         {
229                 return new MainMemoryBlockExtension(this, expression, context);
230         }
231
232         @Override
233         public IProcess getProcess()
234         {
235                 return null;
236         }
237
238         @Override
239         public boolean hasThreads() throws DebugException
240         {
241                 return USE_PSEUDO_THREAD;
242         }
243
244         @Override
245         public IThread[] getThreads() throws DebugException
246         {
247                 return thread == null ? new IThread[0] : new IThread[] { thread };
248         }
249
250         public void addExecutionSpeedListener(Consumer<Double> executionSpeedListener)
251         {
252                 executionSpeedListeners.add(executionSpeedListener);
253         }
254
255         public void removeExecutionSpeedListener(Consumer<Double> executionSpeedListener)
256         {
257                 executionSpeedListeners.remove(executionSpeedListener);
258         }
259
260         private void callExecutionSpeedListener(double executionSpeed)
261         {
262                 executionSpeedListeners.forEach(l -> l.accept(executionSpeed));
263         }
264
265         @SuppressWarnings("unchecked")
266         @Override
267         public <T> T getAdapter(Class<T> adapter)
268         {
269                 if (adapter == IDebugElement.class)
270                         return (T) this;
271
272                 // leave this here; maybe we implement IStepFilters someday
273                 if (adapter == IStepFilters.class)
274                         if (this instanceof IStepFilters)
275                                 return (T) getDebugTarget();
276
277                 if (adapter == IDebugTarget.class)
278                         return (T) getDebugTarget();
279
280                 if (adapter == ILaunch.class)
281                         return (T) getLaunch();
282
283                 // CONTEXTLAUNCHING
284                 if (adapter == ILaunchConfiguration.class)
285                         return (T) getLaunch().getLaunchConfiguration();
286
287                 return super.getAdapter(adapter);
288         }
289
290         /**
291          * Fires a creation event for this debug element.
292          */
293         private void fireCreationEvent()
294         {
295                 fireEvent(new DebugEvent(this, DebugEvent.CREATE));
296         }
297
298         /**
299          * Fires a resume for this debug element with the specified detail code.
300          *
301          * @param detail detail code for the resume event, such as <code>DebugEvent.STEP_OVER</code>
302          */
303         private void fireResumeEvent(int detail)
304         {
305                 fireEvent(new DebugEvent(this, DebugEvent.RESUME, detail));
306         }
307
308         /**
309          * Fires a suspend event for this debug element with the specified detail code.
310          *
311          * @param detail detail code for the suspend event, such as <code>DebugEvent.BREAKPOINT</code>
312          */
313         private void fireSuspendEvent(int detail)
314         {
315                 fireEvent(new DebugEvent(this, DebugEvent.SUSPEND, detail));
316         }
317
318         /**
319          * Fires a terminate event for this debug element.
320          */
321         private void fireTerminateEvent()
322         {
323                 fireEvent(new DebugEvent(this, DebugEvent.TERMINATE));
324         }
325
326         /**
327          * Fires a debug event.
328          *
329          * @param event debug event to fire
330          */
331         private static void fireEvent(DebugEvent event)
332         {
333                 DebugPlugin.getDefault().fireDebugEventSet(new DebugEvent[] { event });
334         }
335
336         private static void throwDebugException(String message) throws DebugException
337         {
338                 throw new DebugException(
339                                 new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, DebugException.TARGET_REQUEST_FAILED, message, null));
340         }
341 }