1
/*******************************************************************************
2
* Copyright (c) 2006, 2010 Wind River Systems and others.
3
* All rights reserved. This program and the accompanying materials
4
* are made available under the terms of the Eclipse Public License v1.0
5
* which accompanies this distribution, and is available at
6
* http://www.eclipse.org/legal/epl-v10.html
9
* IBM Corporation - initial API and implementation
10
* Wind River Systems - initial API and implementation
11
*******************************************************************************/
12
package org.eclipse.cdt.dsf.debug.ui.sourcelookup;
16
import java.util.Arrays;
17
import java.util.HashSet;
19
import java.util.concurrent.ExecutionException;
20
import java.util.concurrent.RejectedExecutionException;
21
import java.util.concurrent.atomic.AtomicBoolean;
23
import org.eclipse.cdt.core.model.ITranslationUnit;
24
import org.eclipse.cdt.debug.internal.core.sourcelookup.CSourceNotFoundElement;
25
import org.eclipse.cdt.debug.internal.ui.sourcelookup.CSourceNotFoundEditorInput;
26
import org.eclipse.cdt.debug.ui.ICDebugUIConstants;
27
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
28
import org.eclipse.cdt.dsf.concurrent.DsfExecutor;
29
import org.eclipse.cdt.dsf.concurrent.DsfRunnable;
30
import org.eclipse.cdt.dsf.concurrent.Query;
31
import org.eclipse.cdt.dsf.concurrent.ThreadSafe;
32
import org.eclipse.cdt.dsf.datamodel.DMContexts;
33
import org.eclipse.cdt.dsf.datamodel.IDMContext;
34
import org.eclipse.cdt.dsf.debug.service.IRunControl;
35
import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext;
36
import org.eclipse.cdt.dsf.debug.service.IRunControl.StateChangeReason;
37
import org.eclipse.cdt.dsf.debug.service.IStack;
38
import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext;
39
import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMData;
40
import org.eclipse.cdt.dsf.debug.sourcelookup.DsfSourceLookupParticipant;
41
import org.eclipse.cdt.dsf.debug.ui.viewmodel.SteppingController;
42
import org.eclipse.cdt.dsf.debug.ui.viewmodel.SteppingController.ISteppingControlParticipant;
43
import org.eclipse.cdt.dsf.debug.ui.viewmodel.SteppingController.SteppingTimedOutEvent;
44
import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin;
45
import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
46
import org.eclipse.cdt.dsf.service.DsfServicesTracker;
47
import org.eclipse.cdt.dsf.service.DsfSession;
48
import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext;
49
import org.eclipse.core.filesystem.EFS;
50
import org.eclipse.core.filesystem.IFileStore;
51
import org.eclipse.core.resources.IFile;
52
import org.eclipse.core.runtime.CoreException;
53
import org.eclipse.core.runtime.IProgressMonitor;
54
import org.eclipse.core.runtime.IStatus;
55
import org.eclipse.core.runtime.Status;
56
import org.eclipse.core.runtime.jobs.Job;
57
import org.eclipse.debug.core.sourcelookup.ISourceLookupDirector;
58
import org.eclipse.debug.core.sourcelookup.ISourceLookupParticipant;
59
import org.eclipse.debug.core.sourcelookup.containers.LocalFileStorage;
60
import org.eclipse.debug.ui.DebugUITools;
61
import org.eclipse.debug.ui.ISourcePresentation;
62
import org.eclipse.debug.ui.sourcelookup.ISourceDisplay;
63
import org.eclipse.jface.text.BadLocationException;
64
import org.eclipse.jface.text.IDocument;
65
import org.eclipse.jface.text.IRegion;
66
import org.eclipse.jface.text.ITextOperationTarget;
67
import org.eclipse.jface.text.ITextViewer;
68
import org.eclipse.jface.text.ITextViewerExtension5;
69
import org.eclipse.jface.text.Position;
70
import org.eclipse.swt.custom.BusyIndicator;
71
import org.eclipse.swt.custom.StyledText;
72
import org.eclipse.swt.graphics.Rectangle;
73
import org.eclipse.swt.widgets.Display;
74
import org.eclipse.ui.IEditorDescriptor;
75
import org.eclipse.ui.IEditorInput;
76
import org.eclipse.ui.IEditorPart;
77
import org.eclipse.ui.IReusableEditor;
78
import org.eclipse.ui.IWorkbenchPage;
79
import org.eclipse.ui.PartInitException;
80
import org.eclipse.ui.PlatformUI;
81
import org.eclipse.ui.editors.text.EditorsUI;
82
import org.eclipse.ui.ide.FileStoreEditorInput;
83
import org.eclipse.ui.ide.IDE;
84
import org.eclipse.ui.part.FileEditorInput;
85
import org.eclipse.ui.progress.UIJob;
86
import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants;
87
import org.eclipse.ui.texteditor.IDocumentProvider;
88
import org.eclipse.ui.texteditor.ITextEditor;
91
* Source display adapter that performs the source lookup, opens the editor,
92
* and paints the IP for the given object.
94
* The implementation relies on three types of jobs to perform the operations.<br>
95
* - The first kind, "lookup job" performs the source lookup operation. <br>
96
* - The second "display job" positions and annotates the editor. <br>
97
* - The third clears the old IP annotations when a thread or process has resumed
101
* The the lookup jobs can run in parallel with the display or the clearing job,
102
* but the clearing job and the display job must not run at the same time.
103
* Hence there is some involved logic which ensures that the jobs are run in
104
* proper order. To avoid race conditions, this logic uses the session's
105
* dispatch thread to synchronize access to the state data of the running jobs.
108
* Debuggers can override the default source editor used by the source display
109
* adapter by registering their own ISourcePresentation adapter.
112
* @see ISourcePresentation
117
public class DsfSourceDisplayAdapter implements ISourceDisplay, ISteppingControlParticipant
119
private static final class FrameData {
120
IFrameDMContext fDmc;
126
public boolean equals(Object obj) {
131
if (getClass() != obj.getClass())
133
FrameData other = (FrameData) obj;
134
if (!fDmc.equals(other.fDmc))
137
if (other.fFile != null)
139
} else if (!fFile.equals(other.fFile))
145
* Test whether the given frame data instance refers to the very same location.
148
* @return <code>true</code> if the frame data refers to the same location
150
public boolean isIdentical(FrameData frameData) {
151
return equals(frameData) && fLine == frameData.fLine;
156
* A job to perform source lookup on the given DMC.
158
class LookupJob extends Job {
160
private final IWorkbenchPage fPage;
161
private final FrameData fFrameData;
162
private final boolean fEventTriggered;
165
* Constructs a new source lookup job.
167
public LookupJob(FrameData frameData, IWorkbenchPage page, boolean eventTriggered) {
168
super("DSF Source Lookup"); //$NON-NLS-1$
169
setPriority(Job.INTERACTIVE);
171
fFrameData = frameData;
173
fEventTriggered = eventTriggered;
176
IDMContext getDmc() { return fFrameData.fDmc; }
179
protected IStatus run(final IProgressMonitor monitor) {
180
if (monitor.isCanceled()) {
181
return Status.CANCEL_STATUS;
184
final SourceLookupResult result = performLookup();
185
executeFromJob(new DsfRunnable() { public void run() {
186
if (!monitor.isCanceled()) {
187
fPrevResult = result;
188
fPrevFrameData = fFrameData;
189
fRunningLookupJob = null;
190
startDisplayJob(fPrevResult, fFrameData, fPage, fEventTriggered);
193
return Status.OK_STATUS;
196
private SourceLookupResult performLookup() {
197
IDMContext dmc = fFrameData.fDmc;
198
SourceLookupResult result = new SourceLookupResult(dmc , null, null, null);
199
String editorId = null;
200
IEditorInput editorInput = null;
201
Object sourceElement = fSourceLookup.getSourceElement(dmc);
203
if (sourceElement == null) {
204
editorInput = new CSourceNotFoundEditorInput(new CSourceNotFoundElement(dmc, fSourceLookup.getLaunchConfiguration(), fFrameData.fFile));
205
editorId = ICDebugUIConstants.CSOURCENOTFOUND_EDITOR_ID;
207
ISourcePresentation presentation= null;
208
if (fSourceLookup instanceof ISourcePresentation) {
209
presentation = (ISourcePresentation) fSourceLookup;
212
presentation = (ISourcePresentation) dmc.getAdapter(ISourcePresentation.class);
215
if (presentation != null) {
216
editorInput = presentation.getEditorInput(sourceElement);
217
if (editorInput != null) {
218
editorId = presentation.getEditorId(editorInput, sourceElement);
220
} else if (sourceElement instanceof IFile) {
221
editorId = getEditorIdForFilename(((IFile)sourceElement).getName());
222
editorInput = new FileEditorInput((IFile)sourceElement);
223
} else if (sourceElement instanceof ITranslationUnit) {
225
URI uriLocation = ((ITranslationUnit)sourceElement).getLocationURI();
226
IFileStore fileStore = EFS.getStore(uriLocation);
227
editorInput = new FileStoreEditorInput(fileStore);
228
editorId = getEditorIdForFilename(fileStore.getName());
229
} catch (CoreException e) {
230
editorInput = new CSourceNotFoundEditorInput(new CSourceNotFoundElement(dmc, fSourceLookup.getLaunchConfiguration(), fFrameData.fFile));
231
editorId = ICDebugUIConstants.CSOURCENOTFOUND_EDITOR_ID;
233
} else if (sourceElement instanceof LocalFileStorage) {
234
File file = ((LocalFileStorage)sourceElement).getFile();
235
IFileStore fileStore = EFS.getLocalFileSystem().fromLocalFile(file);
236
editorInput = new FileStoreEditorInput(fileStore);
237
editorId = getEditorIdForFilename(file.getName());
240
result.setEditorInput(editorInput);
241
result.setEditorId(editorId);
242
result.setSourceElement(sourceElement);
247
private String getEditorIdForFilename(String filename) {
249
IEditorDescriptor descriptor= IDE.getEditorDescriptor(filename);
250
return descriptor.getId();
251
} catch (PartInitException exc) {
252
DsfUIPlugin.log(exc);
254
return "org.eclipse.ui.DefaultTextEditor"; //$NON-NLS-1$
259
* Job that positions the editor and paints the IP Annotation for given DMC.
261
class DisplayJob extends UIJob {
262
private final SourceLookupResult fResult;
263
private final IWorkbenchPage fPage;
264
private final FrameData fFrameData;
266
private final DsfRunnable fDisplayJobFinishedRunnable = new DsfRunnable() {
268
// If the current display job does not match up with "this", it means that this job got canceled
269
// after it already completed and after this runnable was queued into the dispatch thread.
270
if (fRunningDisplayJob == DisplayJob.this) {
271
fRunningDisplayJob = null;
272
if (fEventTriggered && !fDoneStepping.getAndSet(true)) {
273
doneStepping(fResult.getDmc());
275
serviceDisplayAndClearingJobs();
280
private final AtomicBoolean fDoneStepping = new AtomicBoolean(false);
281
private IRegion fRegion;
282
private ITextViewer fTextViewer;
283
private final boolean fEventTriggered;
285
IDMContext getDmc() { return fResult.getDmc(); }
288
* Constructs a new source display job
290
public DisplayJob(SourceLookupResult result, FrameData frameData, IWorkbenchPage page, boolean eventTriggered) {
291
super("Debug Source Display"); //$NON-NLS-1$
293
setPriority(Job.INTERACTIVE);
295
fFrameData = frameData;
297
fEventTriggered = eventTriggered;
301
public IStatus runInUIThread(final IProgressMonitor monitor) {
303
if (monitor.isCanceled()) {
304
executeFromJob(fDisplayJobFinishedRunnable);
305
return Status.CANCEL_STATUS;
308
if (fRegion != null && fTextViewer != null) {
309
if (fRunningDisplayJob == this) {
310
if (!shouldCancelSelectionChange()) {
311
enableLineBackgroundPainter();
312
fTextViewer.setSelectedRange(fRegion.getOffset(), 0);
314
executeFromJob(fDisplayJobFinishedRunnable);
317
IEditorPart editor = openEditor(fResult, fPage);
318
if (editor == null) {
319
executeFromJob(fDisplayJobFinishedRunnable);
320
return Status.OK_STATUS;
323
ITextEditor textEditor = null;
324
if (editor instanceof ITextEditor) {
325
textEditor = (ITextEditor)editor;
327
textEditor = (ITextEditor) editor.getAdapter(ITextEditor.class);
329
if (textEditor != null) {
330
if (positionEditor(textEditor, fFrameData)) {
331
return Status.OK_STATUS;
334
executeFromJob(fDisplayJobFinishedRunnable);
336
return Status.OK_STATUS;
339
private boolean shouldCancelSelectionChange() {
340
Query<Boolean> delaySelectionChangeQuery = new Query<Boolean>() {
342
protected void execute(DataRequestMonitor<Boolean> rm) {
343
IExecutionDMContext execCtx = DMContexts.getAncestorOfType(fFrameData.fDmc,
344
IExecutionDMContext.class);
346
IRunControl runControl = fServicesTracker.getService(IRunControl.class);
347
rm.setData(runControl != null && execCtx != null
348
&& (fController != null && fController.getPendingStepCount(execCtx) != 0
349
|| runControl.isStepping(execCtx)));
355
fExecutor.execute(delaySelectionChangeQuery);
356
} catch (RejectedExecutionException e) {
361
return delaySelectionChangeQuery.get();
362
} catch (InterruptedException e) {
364
} catch (ExecutionException e) {
370
* Opens the editor used to display the source for an element selected in
371
* this view and returns the editor that was opened or <code>null</code> if
372
* no editor could be opened.
374
private IEditorPart openEditor(SourceLookupResult result, IWorkbenchPage page) {
375
IEditorInput input= result.getEditorInput();
376
String id= result.getEditorId();
377
if (input == null || id == null) {
381
return openEditor(page, input, id);
385
* Opens an editor in the workbench and returns the editor that was opened
386
* or <code>null</code> if an error occurred while attempting to open the
389
private IEditorPart openEditor(final IWorkbenchPage page, final IEditorInput input, final String id) {
390
final IEditorPart[] editor = new IEditorPart[] {null};
391
Runnable r = new Runnable() {
393
if (!page.getWorkbenchWindow().getWorkbench().isClosing()) {
395
if (input instanceof CSourceNotFoundEditorInput)
396
{ // Don't open additional source not found editors if
397
// there is one to reuse.
398
editor[0] = page.openEditor(input, id, false, IWorkbenchPage.MATCH_ID);
399
if (editor[0] instanceof IReusableEditor) {
400
IReusableEditor re = (IReusableEditor)editor[0];
401
if (! input.equals(re.getEditorInput()))
406
editor[0] = page.openEditor(input, id, false);
407
} catch (PartInitException e) {}
411
BusyIndicator.showWhile(Display.getDefault(), r);
416
* Positions the text editor for the given stack frame
418
private boolean positionEditor(ITextEditor editor, final FrameData frameData) {
419
// Position and annotate the editor.
420
fRegion= getLineInformation(editor, frameData.fLine);
421
if (fRegion != null) {
423
fIPManager.addAnnotation(
424
editor, frameData.fDmc, new Position(fRegion.getOffset(), fRegion.getLength()),
425
frameData.fLevel == 0);
427
// this is a dirty trick to get access to the ITextViewer of the editor
428
Object tot = editor.getAdapter(ITextOperationTarget.class);
429
if (tot instanceof ITextViewer) {
430
fTextViewer = (ITextViewer)tot;
431
int widgetLine = frameData.fLine;
432
if (tot instanceof ITextViewerExtension5) {
433
ITextViewerExtension5 ext5 = (ITextViewerExtension5) tot;
434
// expand region if collapsed
435
ext5.exposeModelRange(fRegion);
436
widgetLine = ext5.modelLine2WidgetLine(widgetLine);
438
revealLine(fTextViewer, widgetLine);
440
if (fStepCount > 0 && fSelectionChangeDelay > 0) {
441
disableLineBackgroundPainter();
442
// reschedule for selection change
443
schedule(fSelectionChangeDelay);
444
if (!fDoneStepping.getAndSet(true)) {
445
doneStepping(getDmc());
449
enableLineBackgroundPainter();
450
fTextViewer.setSelectedRange(fRegion.getOffset(), 0);
453
editor.selectAndReveal(fRegion.getOffset(), 0);
460
* Scroll the given line into the visible area if it is not yet visible.
462
* @see org.eclipse.jface.text.TextViewer#revealRange(int, int)
464
private void revealLine(ITextViewer viewer, int focusLine) {
465
StyledText textWidget = viewer.getTextWidget();
466
int top = textWidget.getTopIndex();
470
int lines = getEstimatedVisibleLinesInViewport(textWidget);
471
int bottom = top + lines;
473
int bottomBuffer = Math.max(1, lines / 3);
475
if (focusLine >= top && focusLine <= bottom - bottomBuffer) {
476
// do not scroll at all as it is already visible
478
if (focusLine > bottom - bottomBuffer && focusLine <= bottom) {
479
// focusLine is already in bottom bufferZone
480
// scroll to top of bottom bufferzone - for smooth down-scrolling
481
int scrollDelta = focusLine - (bottom - bottomBuffer);
482
textWidget.setTopIndex(top + scrollDelta);
484
// scroll to top of visible area minus buffer zone
485
int topBuffer = lines / 3;
486
textWidget.setTopIndex(Math.max(0, focusLine - topBuffer));
493
* @return the number of visible lines in the view port assuming a constant
496
private int getEstimatedVisibleLinesInViewport(StyledText textWidget) {
497
if (textWidget != null) {
498
Rectangle clArea= textWidget.getClientArea();
499
if (!clArea.isEmpty())
500
return clArea.height / textWidget.getLineHeight();
506
* Returns the line information for the given line in the given editor
508
private IRegion getLineInformation(ITextEditor editor, int lineNumber) {
509
IDocumentProvider provider= editor.getDocumentProvider();
510
IEditorInput input= editor.getEditorInput();
512
provider.connect(input);
513
} catch (CoreException e) {
517
IDocument document= provider.getDocument(input);
518
if (document != null)
519
return document.getLineInformation(lineNumber);
520
} catch (BadLocationException e) {
522
provider.disconnect(input);
530
* Job that removes the old IP Annotations associated with given execution
533
class ClearingJob extends UIJob {
534
Set<IRunControl.IExecutionDMContext> fDmcsToClear;
536
public ClearingJob(Set<IRunControl.IExecutionDMContext> dmcs) {
537
super("Debug Source Display"); //$NON-NLS-1$
539
setPriority(Job.INTERACTIVE);
544
* @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor)
547
public IStatus runInUIThread(IProgressMonitor monitor) {
548
DsfRunnable clearingJobFinishedRunnable = new DsfRunnable() { public void run() {
549
assert fRunningClearingJob == ClearingJob.this;
550
fRunningClearingJob = null;
551
serviceDisplayAndClearingJobs();
554
enableLineBackgroundPainter();
556
if (monitor.isCanceled()) {
557
executeFromJob(clearingJobFinishedRunnable);
558
return Status.CANCEL_STATUS;
561
for (IRunControl.IExecutionDMContext dmc : fDmcsToClear) {
562
fIPManager.removeAnnotations(dmc);
565
executeFromJob(clearingJobFinishedRunnable);
566
return Status.OK_STATUS;
570
private static final boolean DEBUG = false;
572
private DsfSession fSession;
573
private DsfExecutor fExecutor;
574
private DsfServicesTracker fServicesTracker;
575
private FrameData fPrevFrameData;
576
private SourceLookupResult fPrevResult;
577
private ISourceLookupDirector fSourceLookup;
578
private DsfSourceLookupParticipant fSourceLookupParticipant;
579
private InstructionPointerManager fIPManager;
581
private LookupJob fRunningLookupJob;
582
private DisplayJob fRunningDisplayJob;
583
private DisplayJob fPendingDisplayJob;
584
private ClearingJob fRunningClearingJob;
585
private Set<IRunControl.IExecutionDMContext> fPendingExecDmcsToClear = new HashSet<IRunControl.IExecutionDMContext>();
586
private SteppingController fController;
588
/** Delay (in milliseconds) before the selection is changed to the IP location */
589
private int fSelectionChangeDelay = 150;
591
private long fStepStartTime = 0;
592
private long fLastStepTime = 0;
593
private long fStepCount;
595
private boolean fEnableLineBackgroundPainter;
597
public DsfSourceDisplayAdapter(DsfSession session, ISourceLookupDirector sourceLocator) {
598
this(session, sourceLocator, null);
604
public DsfSourceDisplayAdapter(DsfSession session, ISourceLookupDirector sourceLocator, SteppingController controller) {
606
fExecutor = session.getExecutor();
607
fServicesTracker = new DsfServicesTracker(DsfUIPlugin.getBundleContext(), session.getId());
608
fSourceLookup = sourceLocator;
609
fSourceLookupParticipant = new DsfSourceLookupParticipant(session);
610
fSourceLookup.addParticipants(new ISourceLookupParticipant[] {fSourceLookupParticipant} );
612
final IInstructionPointerPresentation ipPresentation = (IInstructionPointerPresentation) session.getModelAdapter(IInstructionPointerPresentation.class);
613
fIPManager = new InstructionPointerManager(ipPresentation);
615
fExecutor.execute(new DsfRunnable() { public void run() {
616
fSession.addServiceEventListener(DsfSourceDisplayAdapter.this, null);
619
fController = controller;
620
if (fController != null) {
621
fController.addSteppingControlParticipant(this);
626
* Configure the delay (in milliseconds) before the selection in the editor
627
* is changed to the IP location.
629
* @param delay the delay in milliseconds, a non-negative integer
633
public void setSelectionChangeDelay(int delay) {
634
fSelectionChangeDelay = delay;
637
public void dispose() {
638
if (fController != null) {
639
fController.removeSteppingControlParticipant(this);
644
fExecutor.execute(new DsfRunnable() { public void run() {
645
fSession.removeServiceEventListener(DsfSourceDisplayAdapter.this);
647
} catch (RejectedExecutionException e) {
648
// Session is shut down.
651
fServicesTracker.dispose();
652
fSourceLookup.removeParticipants(new ISourceLookupParticipant[] {fSourceLookupParticipant});
654
// fSourceLookupParticipant is disposed by the source lookup director
656
// Need to remove annotations in UI thread.
657
Display display = Display.getDefault();
658
if (!display.isDisposed()) {
659
display.asyncExec(new Runnable() {
661
enableLineBackgroundPainter();
662
fIPManager.removeAllAnnotations();
668
* @see org.eclipse.debug.ui.contexts.ISourceDisplayAdapter#displaySource(java.lang.Object, org.eclipse.ui.IWorkbenchPage, boolean)
670
public void displaySource(Object context, final IWorkbenchPage page,
671
final boolean force) {
674
IFrameDMContext displayFrame = null;
675
if (context instanceof IDMVMContext) {
676
IDMContext dmc = ((IDMVMContext) context).getDMContext();
677
if (dmc instanceof IFrameDMContext)
678
displayFrame = (IFrameDMContext) dmc;
679
} else if (context instanceof IFrameDMContext)
680
displayFrame = (IFrameDMContext) context;
682
// Quick test. DMC is checked again in source lookup participant, but
683
// it's much quicker to test here.
684
if (displayFrame != null)
685
doDisplaySource(displayFrame, page, force, false);
688
private void doDisplaySource(final IFrameDMContext context, final IWorkbenchPage page, final boolean force, final boolean eventTriggered) {
689
if (DEBUG) System.out.println("[DsfSourceDisplayAdapter] doDisplaySource ctx="+context+" eventTriggered="+eventTriggered); //$NON-NLS-1$ //$NON-NLS-2$
690
if (context.getLevel() < 0) {
693
// Re-dispatch to executor thread before accessing job lists.
694
fExecutor.execute(new DsfRunnable() { public void run() {
695
// We need to retrieve the frame level and line number from the service.
696
IStack stackService = fServicesTracker.getService(IStack.class);
697
if (stackService == null) {
700
stackService.getFrameData(
702
new DataRequestMonitor<IFrameDMData>(fExecutor, null) {
704
public void handleSuccess() {
705
FrameData frameData = new FrameData();
706
frameData.fDmc = context;
707
frameData.fLevel = context.getLevel();
708
// Document line numbers are 0-based. While debugger line numbers are 1-based.
709
IFrameDMData data = getData();
710
frameData.fLine = data.getLine() - 1;
711
frameData.fFile = data.getFile();
712
if (!force && frameData.equals(fPrevFrameData)) {
713
fPrevResult.updateArtifact(context);
714
startDisplayJob(fPrevResult, frameData, page, eventTriggered);
716
startLookupJob(frameData, page, eventTriggered);
720
protected void handleFailure() {
721
doneStepping(context);
725
protected void handleRejectedExecutionException() {
726
doneStepping(context);
732
private void executeFromJob(Runnable runnable) {
734
fExecutor.execute(runnable);
735
} catch (RejectedExecutionException e) {
736
// Session disposed, ignore
740
private void startLookupJob(final FrameData frameData, final IWorkbenchPage page, boolean eventTriggered) {
741
// If there is a previous lookup job running, cancel it.
742
if (fRunningLookupJob != null) {
743
fRunningLookupJob.cancel();
744
if (!eventTriggered && frameData.isIdentical(fRunningLookupJob.fFrameData)) {
745
// identical location - we are done
748
// cancel running lookup job
749
fRunningLookupJob.cancel();
750
// make sure doneStepping() is called even if the job never ran - bug 325394
751
if (fRunningLookupJob.fEventTriggered) {
752
// ... but not if this request is event-triggered for the same context (duplicate suspended event)
753
if (!eventTriggered || !fRunningLookupJob.getDmc().equals(frameData.fDmc)) {
754
doneStepping(fRunningLookupJob.getDmc());
759
fRunningLookupJob = new LookupJob(frameData, page, eventTriggered);
760
fRunningLookupJob.schedule();
763
// To be called only on dispatch thread.
764
private void startDisplayJob(SourceLookupResult lookupResult, FrameData frameData, IWorkbenchPage page, boolean eventTriggered) {
765
DisplayJob nextDisplayJob = new DisplayJob(lookupResult, frameData, page, eventTriggered);
766
if (fRunningDisplayJob != null) {
767
fPendingDisplayJob = null;
768
IExecutionDMContext[] execCtxs = DMContexts.getAllAncestorsOfType(frameData.fDmc, IExecutionDMContext.class);
769
fPendingExecDmcsToClear.removeAll(Arrays.asList(execCtxs));
771
if (!eventTriggered && frameData.isIdentical(fRunningDisplayJob.fFrameData)) {
772
// identical location - we are done
775
// cancel running display job
776
fRunningDisplayJob.cancel();
777
// make sure doneStepping() is called even if the job never ran - bug 325394
778
if (fRunningDisplayJob.fEventTriggered && !fRunningDisplayJob.fDoneStepping.getAndSet(true)) {
779
// ... but not if this request is event-triggered for the same context (duplicate suspended event)
780
if (!eventTriggered || !fRunningDisplayJob.getDmc().equals(lookupResult.getDmc())) {
781
doneStepping(fRunningDisplayJob.getDmc());
785
if (fRunningClearingJob != null) {
786
// Wait for the clearing job to finish, instead, set the
787
// display job as pending.
788
fPendingDisplayJob = nextDisplayJob;
790
fRunningDisplayJob = nextDisplayJob;
791
fRunningDisplayJob.schedule();
795
private void doneStepping(IDMContext context) {
796
if (fController != null) {
797
// indicate completion of step
798
final IExecutionDMContext dmc = DMContexts.getAncestorOfType(context, IExecutionDMContext.class);
801
fController.getExecutor().execute(new DsfRunnable() {
803
fController.doneStepping(dmc, DsfSourceDisplayAdapter.this);
806
} catch (RejectedExecutionException e) {
807
// Session is shutdown
813
private void serviceDisplayAndClearingJobs() {
814
if (!fPendingExecDmcsToClear.isEmpty()) {
815
// There are annotations to be cleared, run the job first
816
fRunningClearingJob = new ClearingJob(fPendingExecDmcsToClear);
817
fRunningClearingJob.schedule();
818
fPendingExecDmcsToClear = new HashSet<IRunControl.IExecutionDMContext>();
819
} else if (fPendingDisplayJob != null) {
820
fRunningDisplayJob = fPendingDisplayJob;
821
fRunningDisplayJob.schedule();
822
fPendingDisplayJob = null;
826
private void startAnnotationClearingJob(IRunControl.IExecutionDMContext execDmc) {
827
// Make sure to add the context to the list.
828
fPendingExecDmcsToClear.add(execDmc);
830
// If lookup job is running, check it against the execution context,
831
// and cancel it if matches.
832
if (fRunningLookupJob != null) {
833
if (DMContexts.isAncestorOf(fRunningLookupJob.getDmc(), execDmc)) {
834
fRunningLookupJob.cancel();
835
fRunningLookupJob = null;
838
// If there is a pending display job, make sure it doesn't get
839
// preempted by this event. If so, just cancel the pending
841
if (fPendingDisplayJob != null) {
842
if (DMContexts.isAncestorOf(fPendingDisplayJob.getDmc(), execDmc)) {
843
fPendingDisplayJob = null;
847
// If no display or clearing jobs are running, schedule the clearing job.
848
if (fRunningClearingJob == null && fRunningDisplayJob == null) {
849
fRunningClearingJob = new ClearingJob(fPendingExecDmcsToClear);
850
fRunningClearingJob.schedule();
851
fPendingExecDmcsToClear = new HashSet<IRunControl.IExecutionDMContext>();
855
@DsfServiceEventHandler
856
public void eventDispatched(IRunControl.IResumedDMEvent e) {
857
if (e.getReason() != StateChangeReason.STEP) {
858
startAnnotationClearingJob(e.getDMContext());
862
@DsfServiceEventHandler
863
public void eventDispatched(IRunControl.IExitedDMEvent e) {
864
startAnnotationClearingJob(e.getDMContext());
870
@DsfServiceEventHandler
871
public void eventDispatched(SteppingTimedOutEvent e) {
872
startAnnotationClearingJob(e.getDMContext());
875
@DsfServiceEventHandler
876
public void eventDispatched(final IRunControl.ISuspendedDMEvent e) {
878
if (e.getReason() == StateChangeReason.STEP || e.getReason() == StateChangeReason.BREAKPOINT) {
879
if (DEBUG) System.out.println("[DsfSourceDisplayAdapter] eventDispatched e="+e); //$NON-NLS-1$
880
// trigger source display immediately (should be optional?)
881
Display.getDefault().asyncExec(new Runnable() {
883
Object context = DebugUITools.getDebugContext();
884
if (context instanceof IDMVMContext) {
885
final IDMContext dmc = ((IDMVMContext)context).getDMContext();
886
if (dmc instanceof IFrameDMContext && DMContexts.isAncestorOf(dmc, e.getDMContext())) {
887
IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
888
doDisplaySource((IFrameDMContext) dmc, page, false, true);
892
doneStepping(e.getDMContext());
895
doneStepping(e.getDMContext());
899
private void updateStepTiming() {
900
long now = System.currentTimeMillis();
901
if (now - fLastStepTime > Math.max(fSelectionChangeDelay, 200)) {
903
fStepStartTime = fLastStepTime = now;
909
long delta = now - fStepStartTime;
910
float meanTime = delta/(float)fStepCount/1000;
911
System.out.println("[DsfSourceDisplayAdapter] step speed = " + 1/meanTime); //$NON-NLS-1$
916
* Disable editor line background painter if it is enabled.
918
* <strong>Must be called on display thread.</strong>
921
private void disableLineBackgroundPainter() {
922
if (!fEnableLineBackgroundPainter) {
923
fEnableLineBackgroundPainter = EditorsUI.getPreferenceStore().getBoolean(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_CURRENT_LINE);
924
if (fEnableLineBackgroundPainter) {
925
EditorsUI.getPreferenceStore().setValue(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_CURRENT_LINE, false);
931
* Enable the editor line background painter if it was enabled before.
933
* <strong>Must be called on display thread.</strong>
936
private void enableLineBackgroundPainter() {
937
if (fEnableLineBackgroundPainter) {
938
fEnableLineBackgroundPainter = false;
939
EditorsUI.getPreferenceStore().setValue(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_CURRENT_LINE, true);