Add support for specifying the debugging step size
[idea/community.git] / java / debugger / impl / src / com / intellij / debugger / impl / DebuggerSession.java
1 /*
2  * Copyright 2000-2014 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.debugger.impl;
17
18 import com.intellij.debugger.DebugEnvironment;
19 import com.intellij.debugger.DebuggerBundle;
20 import com.intellij.debugger.DebuggerInvocationUtil;
21 import com.intellij.debugger.SourcePosition;
22 import com.intellij.debugger.engine.*;
23 import com.intellij.debugger.engine.evaluation.EvaluateException;
24 import com.intellij.debugger.engine.evaluation.EvaluationListener;
25 import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
26 import com.intellij.debugger.engine.jdi.StackFrameProxy;
27 import com.intellij.debugger.engine.requests.RequestManagerImpl;
28 import com.intellij.debugger.jdi.StackFrameProxyImpl;
29 import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
30 import com.intellij.debugger.ui.breakpoints.Breakpoint;
31 import com.intellij.debugger.ui.breakpoints.BreakpointWithHighlighter;
32 import com.intellij.debugger.ui.breakpoints.LineBreakpoint;
33 import com.intellij.execution.ExecutionException;
34 import com.intellij.execution.ExecutionResult;
35 import com.intellij.execution.configurations.RemoteConnection;
36 import com.intellij.execution.configurations.RemoteState;
37 import com.intellij.execution.configurations.RunProfileState;
38 import com.intellij.execution.process.ProcessHandler;
39 import com.intellij.execution.process.ProcessOutputTypes;
40 import com.intellij.idea.ActionsBundle;
41 import com.intellij.openapi.application.ApplicationManager;
42 import com.intellij.openapi.application.ModalityState;
43 import com.intellij.openapi.diagnostic.Logger;
44 import com.intellij.openapi.editor.Document;
45 import com.intellij.openapi.project.Project;
46 import com.intellij.openapi.ui.Messages;
47 import com.intellij.openapi.util.Comparing;
48 import com.intellij.openapi.util.Computable;
49 import com.intellij.openapi.util.Disposer;
50 import com.intellij.openapi.util.Pair;
51 import com.intellij.openapi.util.registry.Registry;
52 import com.intellij.psi.PsiCompiledElement;
53 import com.intellij.psi.PsiDocumentManager;
54 import com.intellij.psi.PsiFile;
55 import com.intellij.psi.search.GlobalSearchScope;
56 import com.intellij.unscramble.ThreadState;
57 import com.intellij.util.Alarm;
58 import com.intellij.util.TimeoutUtil;
59 import com.intellij.util.ui.UIUtil;
60 import com.intellij.xdebugger.AbstractDebuggerSession;
61 import com.intellij.xdebugger.XDebugSession;
62 import com.intellij.xdebugger.impl.actions.XDebuggerActions;
63 import com.intellij.xdebugger.impl.evaluate.quick.common.ValueLookupManager;
64 import com.sun.jdi.ObjectCollectedException;
65 import com.sun.jdi.ThreadReference;
66 import com.sun.jdi.event.Event;
67 import com.sun.jdi.request.EventRequest;
68 import com.sun.jdi.request.StepRequest;
69 import org.jetbrains.annotations.NotNull;
70 import org.jetbrains.annotations.Nullable;
71
72 import java.util.Collection;
73 import java.util.HashSet;
74 import java.util.List;
75 import java.util.Set;
76
77 public class DebuggerSession implements AbstractDebuggerSession {
78   private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.impl.DebuggerSession");
79   // flags
80   private final MyDebuggerStateManager myContextManager;
81
82   public static final int STATE_STOPPED = 0;
83   public static final int STATE_RUNNING = 1;
84   public static final int STATE_WAITING_ATTACH = 2;
85   public static final int STATE_PAUSED = 3;
86   public static final int STATE_WAIT_EVALUATION = 5;
87   public static final int STATE_DISPOSED = 6;
88
89   public static final int EVENT_ATTACHED = 0;
90   public static final int EVENT_DETACHED = 1;
91   public static final int EVENT_RESUME = 4;
92   public static final int EVENT_STEP = 5;
93   public static final int EVENT_PAUSE = 6;
94   public static final int EVENT_REFRESH = 7;
95   public static final int EVENT_CONTEXT = 8;
96   public static final int EVENT_START_WAIT_ATTACH = 9;
97   public static final int EVENT_DISPOSE = 10;
98   public static final int EVENT_REFRESH_VIEWS_ONLY = 11;
99   public static final int EVENT_THREADS_REFRESH = 12;
100
101   private volatile boolean myIsEvaluating;
102   private volatile int myIgnoreFiltersFrameCountThreshold = 0;
103
104   private DebuggerSessionState myState = null;
105
106   private final String mySessionName;
107   private final DebugProcessImpl myDebugProcess;
108   private @NotNull GlobalSearchScope mySearchScope;
109
110   private final DebuggerContextImpl SESSION_EMPTY_CONTEXT;
111   //Thread, user is currently stepping through
112   private final Set<ThreadReferenceProxyImpl> mySteppingThroughThreads = new HashSet<ThreadReferenceProxyImpl>();
113   protected final Alarm myUpdateAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD);
114
115   private boolean myModifiedClassesScanRequired = false;
116
117   public boolean isSteppingThrough(ThreadReferenceProxyImpl threadProxy) {
118     return mySteppingThroughThreads.contains(threadProxy);
119   }
120
121   @NotNull
122   public GlobalSearchScope getSearchScope() {
123     //noinspection ConstantConditions
124     LOG.assertTrue(mySearchScope != null, "Accessing Session's search scope before its initialization");
125     return mySearchScope;
126   }
127
128   public boolean isModifiedClassesScanRequired() {
129     return myModifiedClassesScanRequired;
130   }
131
132   public void setModifiedClassesScanRequired(boolean modifiedClassesScanRequired) {
133     myModifiedClassesScanRequired = modifiedClassesScanRequired;
134   }
135
136   private class MyDebuggerStateManager extends DebuggerStateManager {
137     private DebuggerContextImpl myDebuggerContext;
138
139     MyDebuggerStateManager() {
140       myDebuggerContext = SESSION_EMPTY_CONTEXT;
141     }
142
143     @Override
144     public DebuggerContextImpl getContext() {
145       return myDebuggerContext;
146     }
147
148     /**
149      * actually state changes not in the same sequence as you call setState
150      * the 'resuming' setState with context.getSuspendContext() == null may be set prior to
151      * the setState for the context with context.getSuspendContext()
152      *
153      * in this case we assume that the latter setState is ignored
154      * since the thread was resumed
155      */
156     @Override
157     public void setState(final DebuggerContextImpl context, final int state, final int event, final String description) {
158       ApplicationManager.getApplication().assertIsDispatchThread();
159       final DebuggerSession session = context.getDebuggerSession();
160       LOG.assertTrue(session == DebuggerSession.this || session == null);
161       final Runnable setStateRunnable = new Runnable() {
162         @Override
163         public void run() {
164           LOG.assertTrue(myDebuggerContext.isInitialised());
165           myDebuggerContext = context;
166           if (LOG.isDebugEnabled()) {
167             LOG.debug("DebuggerSession state = " + state + ", event = " + event);
168           }
169
170           myIsEvaluating = false;
171
172           myState = new DebuggerSessionState(state, description);
173           fireStateChanged(context, event);
174         }
175       };
176
177       if(context.getSuspendContext() == null) {
178         setStateRunnable.run();
179       }
180       else {
181         getProcess().getManagerThread().schedule(new SuspendContextCommandImpl(context.getSuspendContext()) {
182           @Override
183           public void contextAction() throws Exception {
184             context.initCaches();
185             DebuggerInvocationUtil.swingInvokeLater(getProject(), setStateRunnable);
186           }
187         });
188       }
189     }
190   }
191
192   protected DebuggerSession(String sessionName, final DebugProcessImpl debugProcess) {
193     mySessionName  = sessionName;
194     myDebugProcess = debugProcess;
195     SESSION_EMPTY_CONTEXT = DebuggerContextImpl.createDebuggerContext(this, null, null, null);
196     myContextManager = new MyDebuggerStateManager();
197     myState = new DebuggerSessionState(STATE_STOPPED, null);
198     myDebugProcess.addDebugProcessListener(new MyDebugProcessListener(debugProcess));
199     myDebugProcess.addEvaluationListener(new MyEvaluationListener());
200     ValueLookupManager.getInstance(getProject()).startListening();
201   }
202
203   @NotNull
204   public DebuggerStateManager getContextManager() {
205     return myContextManager;
206   }
207
208   public Project getProject() {
209     return getProcess().getProject();
210   }
211
212   public String getSessionName() {
213     return mySessionName;
214   }
215
216   public DebugProcessImpl getProcess() {
217     return myDebugProcess;
218   }
219
220   private static class DebuggerSessionState {
221     final int myState;
222     final String myDescription;
223
224     public DebuggerSessionState(int state, String description) {
225       myState = state;
226       myDescription = description;
227     }
228   }
229
230   public int getState() {
231     return myState.myState;
232   }
233
234   public String getStateDescription() {
235     if (myState.myDescription != null) {
236       return myState.myDescription;
237     }
238
239     switch (myState.myState) {
240       case STATE_STOPPED:
241         return DebuggerBundle.message("status.debug.stopped");
242       case STATE_RUNNING:
243         return DebuggerBundle.message("status.app.running");
244       case STATE_WAITING_ATTACH:
245         RemoteConnection connection = getProcess().getConnection();
246         final String addressDisplayName = DebuggerBundle.getAddressDisplayName(connection);
247         final String transportName = DebuggerBundle.getTransportName(connection);
248         return connection.isServerMode() ? DebuggerBundle.message("status.listening", addressDisplayName, transportName) : DebuggerBundle.message("status.connecting", addressDisplayName, transportName);
249       case STATE_PAUSED:
250         return DebuggerBundle.message("status.paused");
251       case STATE_WAIT_EVALUATION:
252         return DebuggerBundle.message("status.waiting.evaluation.result");
253       case STATE_DISPOSED:
254         return DebuggerBundle.message("status.debug.stopped");
255     }
256     return null;
257   }
258
259   /* Stepping */
260   private void resumeAction(final DebugProcessImpl.ResumeCommand command, int event) {
261     getContextManager().setState(SESSION_EMPTY_CONTEXT, STATE_WAIT_EVALUATION, event, null);
262     myDebugProcess.getManagerThread().schedule(command);
263   }
264
265   public void stepOut(int stepSize) {
266     final SuspendContextImpl suspendContext = getSuspendContext();
267     final DebugProcessImpl.ResumeCommand cmd = myDebugProcess.createStepOutCommand(suspendContext, stepSize);
268     mySteppingThroughThreads.add(cmd.getContextThread());
269     resumeAction(cmd, EVENT_STEP);
270   }
271
272   public void stepOut() {
273     stepOut(StepRequest.STEP_LINE);
274   }
275
276   public void stepOver(boolean ignoreBreakpoints, int stepSize) {
277     final SuspendContextImpl suspendContext = getSuspendContext();
278     final DebugProcessImpl.ResumeCommand cmd = myDebugProcess.createStepOverCommand(suspendContext, ignoreBreakpoints, stepSize);
279     mySteppingThroughThreads.add(cmd.getContextThread());
280     resumeAction(cmd, EVENT_STEP);
281   }
282
283   public void stepOver(boolean ignoreBreakpoints) {
284     stepOver(ignoreBreakpoints, StepRequest.STEP_LINE);
285   }
286
287   public void stepInto(final boolean ignoreFilters, final @Nullable MethodFilter smartStepFilter, int stepSize) {
288     final SuspendContextImpl suspendContext = getSuspendContext();
289     final DebugProcessImpl.ResumeCommand cmd = myDebugProcess.createStepIntoCommand(suspendContext, ignoreFilters, smartStepFilter, stepSize);
290     mySteppingThroughThreads.add(cmd.getContextThread());
291     resumeAction(cmd, EVENT_STEP);
292   }
293
294   public void stepInto(final boolean ignoreFilters, final @Nullable MethodFilter smartStepFilter) {
295     stepInto(ignoreFilters, smartStepFilter, StepRequest.STEP_LINE);
296   }
297
298   public void runToCursor(Document document, int line, final boolean ignoreBreakpoints) {
299     try {
300       DebugProcessImpl.ResumeCommand runToCursorCommand = myDebugProcess.createRunToCursorCommand(getSuspendContext(), document, line, ignoreBreakpoints);
301       mySteppingThroughThreads.add(runToCursorCommand.getContextThread());
302       resumeAction(runToCursorCommand, EVENT_STEP);
303     }
304     catch (EvaluateException e) {
305       Messages.showErrorDialog(e.getMessage(), UIUtil.removeMnemonic(ActionsBundle.actionText(XDebuggerActions.RUN_TO_CURSOR)));
306     }
307   }
308
309
310   public void resume() {
311     final SuspendContextImpl suspendContext = getSuspendContext();
312     if(suspendContext != null) {
313       if (suspendContext.getSuspendPolicy() == EventRequest.SUSPEND_ALL) {
314         mySteppingThroughThreads.clear();
315       }
316       else {
317         mySteppingThroughThreads.remove(suspendContext.getThread());
318       }
319       resetIgnoreStepFiltersFlag();
320       resumeAction(myDebugProcess.createResumeCommand(suspendContext), EVENT_RESUME);
321     }
322   }
323
324   private void resetIgnoreStepFiltersFlag() {
325     myIgnoreFiltersFrameCountThreshold = 0;
326   }
327
328   public void setIgnoreStepFiltersFlag(int currentStackFrameCount) {
329     myIgnoreFiltersFrameCountThreshold = currentStackFrameCount;
330   }
331
332   public boolean shouldIgnoreSteppingFilters() {
333     return myIgnoreFiltersFrameCountThreshold > 0;
334   }
335
336   public void pause() {
337     myDebugProcess.getManagerThread().schedule(myDebugProcess.createPauseCommand());
338   }
339
340   /*Presentation*/
341
342   public void showExecutionPoint() {
343     getContextManager().setState(DebuggerContextUtil.createDebuggerContext(this, getSuspendContext()), STATE_PAUSED, EVENT_REFRESH, null);
344   }
345
346   public void refresh(final boolean refreshViewsOnly) {
347     final int state = getState();
348     DebuggerContextImpl context = myContextManager.getContext();
349     DebuggerContextImpl newContext = DebuggerContextImpl.createDebuggerContext(this, context.getSuspendContext(), context.getThreadProxy(), context.getFrameProxy());
350     myContextManager.setState(newContext, state, refreshViewsOnly? EVENT_REFRESH_VIEWS_ONLY : EVENT_REFRESH, null);
351   }
352
353   public void dispose() {
354     getProcess().dispose();
355     Disposer.dispose(myUpdateAlarm);
356     DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() {
357       @Override
358       public void run() {
359         getContextManager().setState(SESSION_EMPTY_CONTEXT, STATE_DISPOSED, EVENT_DISPOSE, null);
360       }
361     });
362   }
363
364   // ManagerCommands
365   @Override
366   public boolean isStopped() {
367     return getState() == STATE_STOPPED;
368   }
369
370   public boolean isAttached() {
371     return !isStopped() && getState() != STATE_WAITING_ATTACH;
372   }
373
374   @Override
375   public boolean isPaused() {
376     return getState() == STATE_PAUSED;
377   }
378
379   public boolean isConnecting() {
380     return getState() == STATE_WAITING_ATTACH;
381   }
382
383   public boolean isEvaluating() {
384     return myIsEvaluating;
385   }
386
387   public boolean isRunning() {
388     return getState() == STATE_RUNNING && !getProcess().getProcessHandler().isProcessTerminated();
389   }
390
391   private SuspendContextImpl getSuspendContext() {
392     ApplicationManager.getApplication().assertIsDispatchThread();
393     return getContextManager().getContext().getSuspendContext();
394   }
395
396   @Nullable
397   protected ExecutionResult attach(DebugEnvironment environment) throws ExecutionException {
398     RemoteConnection remoteConnection = environment.getRemoteConnection();
399     final String addressDisplayName = DebuggerBundle.getAddressDisplayName(remoteConnection);
400     final String transportName = DebuggerBundle.getTransportName(remoteConnection);
401     mySearchScope = environment.getSearchScope();
402     final ExecutionResult executionResult = myDebugProcess.attachVirtualMachine(environment, this);
403     getContextManager().setState(SESSION_EMPTY_CONTEXT, STATE_WAITING_ATTACH, EVENT_START_WAIT_ATTACH, DebuggerBundle.message("status.waiting.attach", addressDisplayName, transportName));
404     return executionResult;
405   }
406
407   private class MyDebugProcessListener extends DebugProcessAdapterImpl {
408     private final DebugProcessImpl myDebugProcess;
409
410     public MyDebugProcessListener(final DebugProcessImpl debugProcess) {
411       myDebugProcess = debugProcess;
412     }
413
414     //executed in manager thread
415     @Override
416     public void connectorIsReady() {
417       DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() {
418         @Override
419         public void run() {
420           RemoteConnection connection = myDebugProcess.getConnection();
421           final String addressDisplayName = DebuggerBundle.getAddressDisplayName(connection);
422           final String transportName = DebuggerBundle.getTransportName(connection);
423           final String connectionStatusMessage = connection.isServerMode() ? DebuggerBundle.message("status.listening", addressDisplayName, transportName) : DebuggerBundle.message("status.connecting", addressDisplayName, transportName);
424           getContextManager().setState(SESSION_EMPTY_CONTEXT, STATE_WAITING_ATTACH, EVENT_START_WAIT_ATTACH, connectionStatusMessage);
425         }
426       });
427     }
428
429     @Override
430     public void paused(final SuspendContextImpl suspendContext) {
431       if (LOG.isDebugEnabled()) {
432         LOG.debug("paused");
433       }
434
435       if (!shouldSetAsActiveContext(suspendContext)) {
436         DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() {
437           @Override
438           public void run() {
439             getContextManager().fireStateChanged(getContextManager().getContext(), EVENT_THREADS_REFRESH);
440           }
441         });
442         return;
443       }
444
445       ThreadReferenceProxyImpl currentThread   = suspendContext.getThread();
446       final StackFrameContext positionContext;
447
448       if (currentThread == null) {
449         //Pause pressed
450         LOG.assertTrue(suspendContext.getSuspendPolicy() == EventRequest.SUSPEND_ALL);
451         SuspendContextImpl oldContext = getProcess().getSuspendManager().getPausedContext();
452
453         if (oldContext != null) {
454           currentThread = oldContext.getThread();
455         }
456
457         if(currentThread == null) {
458           final Collection<ThreadReferenceProxyImpl> allThreads = getProcess().getVirtualMachineProxy().allThreads();
459           // heuristics: try to pre-select EventDispatchThread
460           for (final ThreadReferenceProxyImpl thread : allThreads) {
461             if (ThreadState.isEDT(thread.name())) {
462               currentThread = thread;
463               break;
464             }
465           }
466           if (currentThread == null) {
467             // heuristics: display the first thread with RUNNABLE status
468             for (final ThreadReferenceProxyImpl thread : allThreads) {
469               currentThread = thread;
470               if (currentThread.status() == ThreadReference.THREAD_STATUS_RUNNING) {
471                 break;
472               }
473             }
474           }
475         }
476
477         StackFrameProxyImpl proxy = null;
478         if (currentThread != null) {
479           try {
480             while (!currentThread.isSuspended()) {
481               // wait until thread is considered suspended. Querying data from a thread immediately after VM.suspend()
482               // may result in IncompatibleThreadStateException, most likely some time after suspend() VM erroneously thinks that thread is still running
483               TimeoutUtil.sleep(10);
484             }
485             proxy = (currentThread.frameCount() > 0) ? currentThread.frame(0) : null;
486           }
487           catch (ObjectCollectedException ignored) {
488             proxy = null;
489           }
490           catch (EvaluateException e) {
491             proxy = null;
492             LOG.error(e);
493           }
494         }
495         positionContext = new SimpleStackFrameContext(proxy, myDebugProcess);
496       }
497       else {
498         positionContext = suspendContext;
499       }
500
501       if (currentThread != null) {
502         try {
503           final int frameCount = currentThread.frameCount();
504           if (frameCount == 0 || (frameCount <= myIgnoreFiltersFrameCountThreshold)) {
505             resetIgnoreStepFiltersFlag();
506           }
507         }
508         catch (EvaluateException e) {
509           LOG.info(e);
510           resetIgnoreStepFiltersFlag();
511         }
512       }
513
514       SourcePosition position = PsiDocumentManager.getInstance(getProject()).commitAndRunReadAction(new Computable<SourcePosition>() {
515         @Override
516         public @Nullable SourcePosition compute() {
517           return ContextUtil.getSourcePosition(positionContext);
518         }
519       });
520
521       if (position != null) {
522         final List<Pair<Breakpoint, Event>> eventDescriptors = DebuggerUtilsEx.getEventDescriptors(suspendContext);
523         final RequestManagerImpl requestsManager = suspendContext.getDebugProcess().getRequestsManager();
524         final PsiFile foundFile = position.getFile();
525         final boolean sourceMissing = foundFile instanceof PsiCompiledElement;
526         for (Pair<Breakpoint, Event> eventDescriptor : eventDescriptors) {
527           Breakpoint breakpoint = eventDescriptor.getFirst();
528           if (breakpoint instanceof LineBreakpoint) {
529             final SourcePosition breakpointPosition = ((BreakpointWithHighlighter)breakpoint).getSourcePosition();
530             if (breakpointPosition == null || (!sourceMissing && breakpointPosition.getLine() != position.getLine())) {
531               requestsManager.deleteRequest(breakpoint);
532               requestsManager.setInvalid(breakpoint, DebuggerBundle.message("error.invalid.breakpoint.source.changed"));
533               breakpoint.updateUI();
534             }
535             else if (sourceMissing) {
536               // adjust position to be position of the breakpoint in order to show the real originator of the event
537               position = breakpointPosition;
538               final StackFrameProxy frameProxy = positionContext.getFrameProxy();
539               String className;
540               try {
541                 className = frameProxy != null? frameProxy.location().declaringType().name() : "";
542               }
543               catch (EvaluateException ignored) {
544                 className = "";
545               }
546               requestsManager.setInvalid(breakpoint, DebuggerBundle.message("error.invalid.breakpoint.source.not.found", className));
547               breakpoint.updateUI();
548             }
549           }
550         }
551       }
552
553       final DebuggerContextImpl debuggerContext = DebuggerContextImpl.createDebuggerContext(DebuggerSession.this, suspendContext, currentThread, null);
554       debuggerContext.setPositionCache(position);
555
556       DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() {
557         @Override
558         public void run() {
559           getContextManager().setState(debuggerContext, STATE_PAUSED, EVENT_PAUSE, null);
560         }
561       });
562     }
563
564     private boolean shouldSetAsActiveContext(final SuspendContextImpl suspendContext) {
565       // always switch context if it is not a breakpoint stop
566       if (DebuggerUtilsEx.getEventDescriptors(suspendContext).isEmpty()) {
567         return true;
568       }
569       final ThreadReferenceProxyImpl newThread = suspendContext.getThread();
570       if (newThread == null || suspendContext.getSuspendPolicy() == EventRequest.SUSPEND_ALL || isSteppingThrough(newThread)) {
571         return true;
572       }
573       final SuspendContextImpl currentSuspendContext = getContextManager().getContext().getSuspendContext();
574       if (currentSuspendContext == null) {
575         return true;
576       }
577       if (enableBreakpointsDuringEvaluation()) {
578         final ThreadReferenceProxyImpl currentThread = currentSuspendContext.getThread();
579         return currentThread == null || Comparing.equal(currentThread.getThreadReference(), newThread.getThreadReference());
580       }
581       return false;
582     }
583
584
585     @Override
586     public void resumed(final SuspendContextImpl suspendContext) {
587       final SuspendContextImpl currentContext = getProcess().getSuspendManager().getPausedContext();
588       DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() {
589         @Override
590         public void run() {
591           if (currentContext != null) {
592             getContextManager().setState(DebuggerContextUtil.createDebuggerContext(DebuggerSession.this, currentContext), STATE_PAUSED, EVENT_CONTEXT, null);
593           }
594           else {
595             getContextManager().setState(SESSION_EMPTY_CONTEXT, STATE_RUNNING, EVENT_CONTEXT, null);
596           }
597         }
598       });
599     }
600
601     @Override
602     public void processAttached(final DebugProcessImpl process) {
603       final RemoteConnection connection = getProcess().getConnection();
604       final String addressDisplayName = DebuggerBundle.getAddressDisplayName(connection);
605       final String transportName = DebuggerBundle.getTransportName(connection);
606       final String message = DebuggerBundle.message("status.connected", addressDisplayName, transportName);
607
608       process.printToConsole(message + "\n");
609       DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() {
610         @Override
611         public void run() {
612           getContextManager().setState(SESSION_EMPTY_CONTEXT, STATE_RUNNING, EVENT_ATTACHED, message);
613         }
614       });
615     }
616
617     @Override
618     public void attachException(final RunProfileState state, final ExecutionException exception, final RemoteConnection remoteConnection) {
619       DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() {
620         @Override
621         public void run() {
622           String message = "";
623           if (state instanceof RemoteState) {
624             message = DebuggerBundle.message("status.connect.failed", DebuggerBundle.getAddressDisplayName(remoteConnection), DebuggerBundle.getTransportName(remoteConnection));
625           }
626           message += exception.getMessage();
627           getContextManager().setState(SESSION_EMPTY_CONTEXT, STATE_STOPPED, EVENT_DETACHED, message);
628         }
629       });
630     }
631
632     @Override
633     public void processDetached(final DebugProcessImpl debugProcess, boolean closedByUser) {
634       if (!closedByUser) {
635         ProcessHandler processHandler = debugProcess.getProcessHandler();
636         if(processHandler != null) {
637           final RemoteConnection connection = getProcess().getConnection();
638           final String addressDisplayName = DebuggerBundle.getAddressDisplayName(connection);
639           final String transportName = DebuggerBundle.getTransportName(connection);
640           processHandler.notifyTextAvailable(DebuggerBundle.message("status.disconnected", addressDisplayName, transportName) + "\n", ProcessOutputTypes.SYSTEM);
641         }
642       }
643       DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() {
644         @Override
645         public void run() {
646           final RemoteConnection connection = getProcess().getConnection();
647           final String addressDisplayName = DebuggerBundle.getAddressDisplayName(connection);
648           final String transportName = DebuggerBundle.getTransportName(connection);
649           getContextManager().setState(SESSION_EMPTY_CONTEXT, STATE_STOPPED, EVENT_DETACHED, DebuggerBundle.message("status.disconnected", addressDisplayName, transportName));
650         }
651       });
652       mySteppingThroughThreads.clear();
653     }
654
655     @Override
656     public void threadStarted(DebugProcess proc, ThreadReference thread) {
657       notifyThreadsRefresh();
658     }
659
660     @Override
661     public void threadStopped(DebugProcess proc, ThreadReference thread) {
662       notifyThreadsRefresh();
663     }
664
665     private void notifyThreadsRefresh() {
666       if (!myUpdateAlarm.isDisposed()) {
667         myUpdateAlarm.cancelAllRequests();
668         myUpdateAlarm.addRequest(new Runnable() {
669           @Override
670           public void run() {
671             final DebuggerStateManager contextManager = getContextManager();
672             contextManager.fireStateChanged(contextManager.getContext(), EVENT_THREADS_REFRESH);
673           }
674         }, 100, ModalityState.NON_MODAL);
675       }
676     }
677   }
678
679   private class MyEvaluationListener implements EvaluationListener {
680     @Override
681     public void evaluationStarted(SuspendContextImpl context) {
682       myIsEvaluating = true;
683     }
684
685     @Override
686     public void evaluationFinished(final SuspendContextImpl context) {
687       myIsEvaluating = false;
688       // seems to be not required after move to xdebugger
689       //DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() {
690       //  @Override
691       //  public void run() {
692       //    if (context != getSuspendContext()) {
693       //      getContextManager().setState(DebuggerContextUtil.createDebuggerContext(DebuggerSession.this, context), STATE_PAUSED, EVENT_REFRESH, null);
694       //    }
695       //  }
696       //});
697     }
698   }
699
700   public static boolean enableBreakpointsDuringEvaluation() {
701     return Registry.is("debugger.enable.breakpoints.during.evaluation");
702   }
703
704   @Nullable
705   public XDebugSession getXDebugSession() {
706     JavaDebugProcess process = myDebugProcess.getXdebugProcess();
707     return process != null ? process.getSession() : null;
708   }
709 }