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