test fix
[idea/community.git] / java / debugger / impl / src / com / intellij / debugger / impl / DebuggerSession.java
1 /*
2  * Copyright 2000-2009 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.DebuggerBundle;
19 import com.intellij.debugger.DebuggerInvocationUtil;
20 import com.intellij.debugger.SourcePosition;
21 import com.intellij.debugger.actions.DebuggerActions;
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.requests.RequestManagerImpl;
27 import com.intellij.debugger.jdi.StackFrameProxyImpl;
28 import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
29 import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
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.Executor;
36 import com.intellij.execution.configurations.ModuleRunProfile;
37 import com.intellij.execution.configurations.RemoteConnection;
38 import com.intellij.execution.configurations.RemoteState;
39 import com.intellij.execution.configurations.RunProfileState;
40 import com.intellij.execution.process.ProcessOutputTypes;
41 import com.intellij.execution.runners.ProgramRunner;
42 import com.intellij.idea.ActionsBundle;
43 import com.intellij.openapi.application.ApplicationManager;
44 import com.intellij.openapi.diagnostic.Logger;
45 import com.intellij.openapi.editor.Document;
46 import com.intellij.openapi.module.Module;
47 import com.intellij.openapi.project.Project;
48 import com.intellij.openapi.ui.Messages;
49 import com.intellij.openapi.util.Computable;
50 import com.intellij.openapi.util.Pair;
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.xdebugger.AbstractDebuggerSession;
57 import com.sun.jdi.ObjectCollectedException;
58 import com.sun.jdi.ThreadReference;
59 import com.sun.jdi.event.Event;
60 import com.sun.jdi.request.EventRequest;
61 import org.jetbrains.annotations.NotNull;
62 import org.jetbrains.annotations.Nullable;
63
64 import java.util.Collection;
65 import java.util.HashSet;
66 import java.util.List;
67 import java.util.Set;
68
69 public class DebuggerSession implements AbstractDebuggerSession {
70   private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.impl.DebuggerSession");
71   // flags
72   private final MyDebuggerStateManager myContextManager;
73
74   public static final int STATE_STOPPED = 0;
75   public static final int STATE_RUNNING = 1;
76   public static final int STATE_WAITING_ATTACH = 2;
77   public static final int STATE_PAUSED = 3;
78   public static final int STATE_WAIT_EVALUATION = 5;
79   public static final int STATE_DISPOSED = 6;
80
81   public static final int EVENT_ATTACHED = 0;
82   public static final int EVENT_DETACHED = 1;
83   public static final int EVENT_RESUME = 4;
84   public static final int EVENT_STEP = 5;
85   public static final int EVENT_PAUSE = 6;
86   public static final int EVENT_REFRESH = 7;
87   public static final int EVENT_CONTEXT = 8;
88   public static final int EVENT_START_WAIT_ATTACH = 9;
89   public static final int EVENT_DISPOSE = 10;
90   public static final int EVENT_REFRESH_VIEWS_ONLY = 11;
91
92   private volatile boolean myIsEvaluating;
93   private volatile int myIgnoreFiltersFrameCountThreshold = 0;
94
95   private DebuggerSessionState myState = null;
96
97   private final String mySessionName;
98   private final DebugProcessImpl myDebugProcess;
99   private @NotNull GlobalSearchScope mySearchScope;
100
101   private final DebuggerContextImpl SESSION_EMPTY_CONTEXT;
102   //Thread, user is currently stepping through
103   private final Set<ThreadReferenceProxyImpl> mySteppingThroughThreads = new HashSet<ThreadReferenceProxyImpl>();
104
105   public boolean isSteppingThrough(ThreadReferenceProxyImpl threadProxy) {
106     return mySteppingThroughThreads.contains(threadProxy);
107   }
108
109   @NotNull
110   public GlobalSearchScope getSearchScope() {
111     LOG.assertTrue(mySearchScope != null, "Accessing Session's search scope before its initialization");
112     return mySearchScope;
113   }
114
115   private class MyDebuggerStateManager extends DebuggerStateManager {
116     private DebuggerContextImpl myDebuggerContext;
117
118     MyDebuggerStateManager() {
119       myDebuggerContext = SESSION_EMPTY_CONTEXT;
120     }
121
122     public DebuggerContextImpl getContext() {
123       return myDebuggerContext;
124     }
125
126     /**
127      * actually state changes not in the same sequence as you call setState
128      * the 'resuming' setState with context.getSuspendContext() == null may be set prior to
129      * the setState for the context with context.getSuspendContext()
130      *
131      * in this case we assume that the latter setState is ignored
132      * since the thread was resumed
133      */
134     public void setState(final DebuggerContextImpl context, final int state, final int event, final String description) {
135       ApplicationManager.getApplication().assertIsDispatchThread();
136       LOG.assertTrue(context.getDebuggerSession() == DebuggerSession.this || context.getDebuggerSession() == null);
137       final Runnable setStateRunnable = new Runnable() {
138         public void run() {
139           LOG.assertTrue(myDebuggerContext.isInitialised());
140           myDebuggerContext = context;
141           if (LOG.isDebugEnabled()) {
142             LOG.debug("DebuggerSession state = " + state + ", event = " + event);
143           }
144
145           myIsEvaluating = false;
146
147           myState = new DebuggerSessionState(state, description);
148           fireStateChanged(context, event);
149         }
150       };
151
152       if(context.getSuspendContext() == null) {
153         setStateRunnable.run();
154       }
155       else {
156         getProcess().getManagerThread().schedule(new SuspendContextCommandImpl(context.getSuspendContext()) {
157           public void contextAction() throws Exception {
158             context.initCaches();
159             DebuggerInvocationUtil.swingInvokeLater(getProject(), setStateRunnable);
160           }
161         });
162       }
163     }
164   }
165
166   protected DebuggerSession(String sessionName, final DebugProcessImpl debugProcess) {
167     mySessionName  = sessionName;
168     myDebugProcess = debugProcess;
169     SESSION_EMPTY_CONTEXT = DebuggerContextImpl.createDebuggerContext(this, null, null, null);
170     myContextManager = new MyDebuggerStateManager();
171     myState = new DebuggerSessionState(STATE_STOPPED, null);
172     myDebugProcess.addDebugProcessListener(new MyDebugProcessListener(debugProcess));
173     myDebugProcess.addEvaluationListener(new MyEvaluationListener());
174   }
175
176   public DebuggerStateManager getContextManager() {
177     return myContextManager;
178   }
179
180   public Project getProject() {
181     return getProcess().getProject();
182   }
183
184   public String getSessionName() {
185     return mySessionName;
186   }
187
188   public DebugProcessImpl getProcess() {
189     return myDebugProcess;
190   }
191
192   private static class DebuggerSessionState {
193     final int myState;
194     final String myDescription;
195
196     public DebuggerSessionState(int state, String description) {
197       myState = state;
198       myDescription = description;
199     }
200   }
201
202   public int getState() {
203     return myState.myState;
204   }
205
206   public String getStateDescription() {
207     if (myState.myDescription != null) {
208       return myState.myDescription;
209     }
210
211     switch (myState.myState) {
212       case STATE_STOPPED:
213         return DebuggerBundle.message("status.debug.stopped");
214       case STATE_RUNNING:
215         return DebuggerBundle.message("status.app.running");
216       case STATE_WAITING_ATTACH:
217         RemoteConnection connection = getProcess().getConnection();
218         final String addressDisplayName = DebuggerBundle.getAddressDisplayName(connection);
219         final String transportName = DebuggerBundle.getTransportName(connection);
220         return connection.isServerMode() ? DebuggerBundle.message("status.listening", addressDisplayName, transportName) : DebuggerBundle.message("status.connecting", addressDisplayName, transportName);
221       case STATE_PAUSED:
222         return DebuggerBundle.message("status.paused");
223       case STATE_WAIT_EVALUATION:
224         return DebuggerBundle.message("status.waiting.evaluation.result");
225       case STATE_DISPOSED:
226         return DebuggerBundle.message("status.debug.stopped");
227     }
228     return myState.myDescription;
229   }
230
231   /* Stepping */
232   private void resumeAction(final DebugProcessImpl.ResumeCommand command, int event) {
233     getContextManager().setState(SESSION_EMPTY_CONTEXT, STATE_WAIT_EVALUATION, event, null);
234     myDebugProcess.getManagerThread().schedule(command);
235   }
236
237   public void stepOut() {
238     final SuspendContextImpl suspendContext = getSuspendContext();
239     mySteppingThroughThreads.add(suspendContext.getThread());
240     resumeAction(myDebugProcess.createStepOutCommand(suspendContext), EVENT_STEP);
241   }
242
243   public void stepOver(boolean ignoreBreakpoints) {
244     final SuspendContextImpl suspendContext = getSuspendContext();
245     mySteppingThroughThreads.add(suspendContext.getThread());
246     resumeAction(myDebugProcess.createStepOverCommand(suspendContext, ignoreBreakpoints), EVENT_STEP);
247   }
248
249   public void stepInto(final boolean ignoreFilters, final @Nullable RequestHint.SmartStepFilter smartStepFilter) {
250     final SuspendContextImpl suspendContext = getSuspendContext();
251     mySteppingThroughThreads.add(suspendContext.getThread());
252     resumeAction(myDebugProcess.createStepIntoCommand(suspendContext, ignoreFilters, smartStepFilter), EVENT_STEP);
253   }
254
255   public void runToCursor(Document document, int line, final boolean ignoreBreakpoints) {
256     try {
257       DebugProcessImpl.ResumeCommand runToCursorCommand = myDebugProcess.createRunToCursorCommand(getSuspendContext(), document, line,
258                                                                                              ignoreBreakpoints);
259       mySteppingThroughThreads.add(getSuspendContext().getThread());
260       resumeAction(runToCursorCommand, EVENT_STEP);
261     }
262     catch (EvaluateException e) {
263       Messages.showErrorDialog(e.getMessage(), ActionsBundle.actionText(DebuggerActions.RUN_TO_CURSOR));
264     }
265   }
266
267
268   public void resume() {
269     final SuspendContextImpl suspendContext = getSuspendContext();
270     if(suspendContext != null) {
271       mySteppingThroughThreads.remove(suspendContext.getThread());
272       resetIgnoreStepFiltersFlag();
273       resumeAction(myDebugProcess.createResumeCommand(suspendContext), EVENT_RESUME);
274     }
275   }
276
277   private void resetIgnoreStepFiltersFlag() {
278     myIgnoreFiltersFrameCountThreshold = 0;
279   }
280
281   public void setIgnoreStepFiltersFlag(int currentStackFrameCount) {
282     myIgnoreFiltersFrameCountThreshold = currentStackFrameCount;
283   }
284
285   public boolean shouldIgnoreSteppingFilters() {
286     return myIgnoreFiltersFrameCountThreshold > 0;
287   }
288
289   public void pause() {
290     myDebugProcess.getManagerThread().schedule(myDebugProcess.createPauseCommand());
291   }
292
293   /*Presentation*/
294
295   public void showExecutionPoint() {
296     getContextManager().setState(DebuggerContextUtil.createDebuggerContext(this, getSuspendContext()), STATE_PAUSED, EVENT_REFRESH, null);
297   }
298
299   public void refresh(final boolean refreshViewsOnly) {
300     final int state = getState();
301     DebuggerContextImpl context = myContextManager.getContext();
302     DebuggerContextImpl newContext = DebuggerContextImpl.createDebuggerContext(this, context.getSuspendContext(), context.getThreadProxy(), context.getFrameProxy());
303     myContextManager.setState(newContext, state, refreshViewsOnly? EVENT_REFRESH_VIEWS_ONLY : EVENT_REFRESH, null);
304   }
305
306   public void dispose() {
307     ApplicationManager.getApplication().assertIsDispatchThread();
308     getProcess().dispose();
309     getContextManager().setState(SESSION_EMPTY_CONTEXT, STATE_DISPOSED, EVENT_DISPOSE, null);
310   }
311
312   // ManagerCommands
313   public boolean isStopped() {
314     return getState() == STATE_STOPPED;
315   }
316
317   public boolean isAttached() {
318     return !isStopped() && getState() != STATE_WAITING_ATTACH;
319   }
320
321   public boolean isPaused() {
322     return getState() == STATE_PAUSED;
323   }
324
325   public boolean isConnecting() {
326     return getState() == STATE_WAITING_ATTACH;
327   }
328
329   public boolean isEvaluating() {
330     return myIsEvaluating;
331   }
332
333   public boolean isRunning() {
334     return getState() == STATE_RUNNING && !getProcess().getExecutionResult().getProcessHandler().isProcessTerminated();
335   }
336
337   private SuspendContextImpl getSuspendContext() {
338     ApplicationManager.getApplication().assertIsDispatchThread();
339     return getContextManager().getContext().getSuspendContext();
340   }
341
342   @Nullable
343   protected ExecutionResult attach(@NotNull final Executor executor, @NotNull final ProgramRunner runner, final ModuleRunProfile profile, final RunProfileState state, final RemoteConnection remoteConnection, final boolean pollConnection) throws ExecutionException {
344     final String addressDisplayName = DebuggerBundle.getAddressDisplayName(remoteConnection);
345     final String transportName = DebuggerBundle.getTransportName(remoteConnection);
346     final Module[] modules = profile.getModules();
347     if (modules.length == 0) {
348       mySearchScope = GlobalSearchScope.allScope(getProject());
349     }
350     else {
351       GlobalSearchScope scope = GlobalSearchScope.moduleRuntimeScope(modules[0], true);
352       for (int idx = 1; idx < modules.length; idx++) {
353         Module module = modules[idx];
354         scope = scope.uniteWith(GlobalSearchScope.moduleRuntimeScope(module, true));
355       }
356       mySearchScope = scope;
357     }
358     final ExecutionResult executionResult = myDebugProcess.attachVirtualMachine(executor, runner, this, state, remoteConnection, pollConnection);
359     getContextManager().setState(SESSION_EMPTY_CONTEXT, STATE_WAITING_ATTACH, EVENT_START_WAIT_ATTACH, DebuggerBundle.message("status.waiting.attach", addressDisplayName, transportName));
360     return executionResult;
361   }
362
363   private class MyDebugProcessListener extends DebugProcessAdapterImpl {
364     private final DebugProcessImpl myDebugProcess;
365
366     public MyDebugProcessListener(final DebugProcessImpl debugProcess) {
367       myDebugProcess = debugProcess;
368     }
369
370     //executed in manager thread
371     public void connectorIsReady() {
372       DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() {
373         public void run() {
374           RemoteConnection connection = myDebugProcess.getConnection();
375           final String addressDisplayName = DebuggerBundle.getAddressDisplayName(connection);
376           final String transportName = DebuggerBundle.getTransportName(connection);
377           final String connectionStatusMessage = connection.isServerMode() ? DebuggerBundle.message("status.listening", addressDisplayName, transportName) : DebuggerBundle.message("status.connecting", addressDisplayName, transportName);
378           getContextManager().setState(SESSION_EMPTY_CONTEXT, DebuggerSession.STATE_WAITING_ATTACH, DebuggerSession.EVENT_START_WAIT_ATTACH, connectionStatusMessage);
379         }
380       });
381     }
382
383     public void paused(final SuspendContextImpl suspendContext) {
384       if (LOG.isDebugEnabled()) {
385         LOG.debug("paused");
386       }
387
388       ThreadReferenceProxyImpl currentThread   = suspendContext.getThread();
389       final StackFrameContext positionContext;
390
391       if (currentThread == null) {
392         //Pause pressed
393         LOG.assertTrue(suspendContext.getSuspendPolicy() == EventRequest.SUSPEND_ALL);
394         SuspendContextImpl oldContext = getProcess().getSuspendManager().getPausedContext();
395
396         if (oldContext != null) {
397           currentThread = oldContext.getThread();
398         }
399
400         if(currentThread == null) {
401           final Collection<ThreadReferenceProxyImpl> allThreads = getProcess().getVirtualMachineProxy().allThreads();
402           // heuristics: try to pre-select EventDispatchThread
403           for (final ThreadReferenceProxyImpl thread : allThreads) {
404             if (ThreadState.isEDT(thread.name())) {
405               currentThread = thread;
406               break;
407             }
408           }
409           if (currentThread == null) {
410             // heuristics: display the first thread with RUNNABLE status
411             for (final ThreadReferenceProxyImpl thread : allThreads) {
412               currentThread = thread;
413               if (currentThread.status() == ThreadReference.THREAD_STATUS_RUNNING) {
414                 break;
415               }
416             }
417           }
418         }
419
420         StackFrameProxyImpl proxy = null;
421         if (currentThread != null) {
422           try {
423             while (!currentThread.isSuspended()) {
424               // wait until thread is considered suspended. Querying data from a thread immediately after VM.suspend()
425               // may result in IncompatibleThreadStateException, most likely some time after suspend() VM erroneously thinks that thread is still running
426               try {
427                 Thread.sleep(10);
428               }
429               catch (InterruptedException ignored) {}
430             }
431             proxy = (currentThread.frameCount() > 0) ? currentThread.frame(0) : null;
432           }
433           catch (ObjectCollectedException e) {
434             proxy = null;
435           }
436           catch (EvaluateException e) {
437             proxy = null;
438             LOG.error(e);
439           }
440         }
441         positionContext = new SimpleStackFrameContext(proxy, myDebugProcess);
442       }
443       else {
444         positionContext = suspendContext;
445       }
446
447       if (currentThread != null) {
448         try {
449           final int frameCount = currentThread.frameCount();
450           if (frameCount == 0 || (frameCount < myIgnoreFiltersFrameCountThreshold)) {
451             resetIgnoreStepFiltersFlag();
452           }
453         }
454         catch (EvaluateException e) {
455           LOG.info(e);
456           resetIgnoreStepFiltersFlag();
457         }
458       }
459
460       SourcePosition position = PsiDocumentManager.getInstance(getProject()).commitAndRunReadAction(new Computable<SourcePosition>() {
461         public @Nullable SourcePosition compute() {
462           return ContextUtil.getSourcePosition(positionContext);
463         }
464       });
465
466       if (position != null) {
467         final List<Pair<Breakpoint, Event>> eventDescriptors = DebuggerUtilsEx.getEventDescriptors(suspendContext);
468         final RequestManagerImpl requestsManager = suspendContext.getDebugProcess().getRequestsManager();
469         final PsiFile foundFile = position.getFile();
470         final boolean sourceMissing = foundFile == null || foundFile instanceof PsiCompiledElement;
471         for (Pair<Breakpoint, Event> eventDescriptor : eventDescriptors) {
472           Breakpoint breakpoint = eventDescriptor.getFirst();
473           if (breakpoint instanceof LineBreakpoint) {
474             final SourcePosition breakpointPosition = ((BreakpointWithHighlighter)breakpoint).getSourcePosition();
475             if (breakpointPosition == null || (!sourceMissing && breakpointPosition.getLine() != position.getLine())) {
476               requestsManager.deleteRequest(breakpoint);
477               requestsManager.setInvalid(breakpoint, DebuggerBundle.message("error.invalid.breakpoint.source.changed"));
478               breakpoint.updateUI();
479             }
480             else if (sourceMissing) {
481               // adjust position to be position of the breakpoint in order to show the real originator of the event
482               position = breakpointPosition;
483               String className;
484               try {
485                 className = positionContext.getFrameProxy().location().declaringType().name();
486               }
487               catch (EvaluateException e) {
488                 className = "";
489               }
490               requestsManager.setInvalid(breakpoint, DebuggerBundle.message("error.invalid.breakpoint.source.not.found", className));
491               breakpoint.updateUI();
492             }
493           }
494         }
495       }
496
497       final DebuggerContextImpl debuggerContext = DebuggerContextImpl.createDebuggerContext(DebuggerSession.this, suspendContext, currentThread, null);
498       debuggerContext.setPositionCache(position);
499
500       DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() {
501         public void run() {
502           getContextManager().setState(debuggerContext, STATE_PAUSED, EVENT_PAUSE, null);
503         }
504       });
505     }
506
507     public void resumed(final SuspendContextImpl suspendContext) {
508       final SuspendContextImpl currentContext = getProcess().getSuspendManager().getPausedContext();
509       DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() {
510         public void run() {
511           if (currentContext != null) {
512             getContextManager().setState(DebuggerContextUtil.createDebuggerContext(DebuggerSession.this, currentContext), STATE_PAUSED, EVENT_CONTEXT, null);
513           }
514           else {
515             getContextManager().setState(SESSION_EMPTY_CONTEXT, STATE_RUNNING, EVENT_CONTEXT, null);
516           }
517         }
518       });
519     }
520
521     public void processAttached(final DebugProcessImpl process) {
522       final RemoteConnection connection = getProcess().getConnection();
523       final String addressDisplayName = DebuggerBundle.getAddressDisplayName(connection);
524       final String transportName = DebuggerBundle.getTransportName(connection);
525       final String message = DebuggerBundle.message("status.connected", addressDisplayName, transportName);
526
527       process.getExecutionResult().getProcessHandler().notifyTextAvailable(message + "\n", ProcessOutputTypes.SYSTEM);
528       DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() {
529         public void run() {
530           getContextManager().setState(SESSION_EMPTY_CONTEXT, STATE_RUNNING, EVENT_ATTACHED, message);
531         }
532       });
533     }
534
535     public void attachException(final RunProfileState state, final ExecutionException exception, final RemoteConnection remoteConnection) {
536       DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() {
537         public void run() {
538           String message = "";
539           if (state instanceof RemoteState) {
540             message = DebuggerBundle.message("status.connect.failed", DebuggerBundle.getAddressDisplayName(remoteConnection), DebuggerBundle.getTransportName(remoteConnection));
541           }
542           message += exception.getMessage();
543           getContextManager().setState(SESSION_EMPTY_CONTEXT, STATE_STOPPED, EVENT_DETACHED, message);
544         }
545       });
546     }
547
548     public void processDetached(final DebugProcessImpl debugProcess, boolean closedByUser) {
549       if (!closedByUser) {
550         ExecutionResult executionResult = debugProcess.getExecutionResult();
551         if(executionResult != null) {
552           final RemoteConnection connection = getProcess().getConnection();
553           final String addressDisplayName = DebuggerBundle.getAddressDisplayName(connection);
554           final String transportName = DebuggerBundle.getTransportName(connection);
555           executionResult.getProcessHandler().notifyTextAvailable(DebuggerBundle.message("status.disconnected", addressDisplayName, transportName) + "\n", ProcessOutputTypes.SYSTEM);
556         }
557       }
558       DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() {
559         public void run() {
560           final RemoteConnection connection = getProcess().getConnection();
561           final String addressDisplayName = DebuggerBundle.getAddressDisplayName(connection);
562           final String transportName = DebuggerBundle.getTransportName(connection);
563           getContextManager().setState(SESSION_EMPTY_CONTEXT, STATE_STOPPED, EVENT_DETACHED, DebuggerBundle.message("status.disconnected", addressDisplayName, transportName));
564         }
565       });
566       mySteppingThroughThreads.clear();
567     }
568
569     public void threadStarted(DebugProcess proc, ThreadReference thread) {
570       ((VirtualMachineProxyImpl)proc.getVirtualMachineProxy()).threadStarted(thread);
571       DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() {
572         public void run() {
573           final DebuggerStateManager contextManager = getContextManager();
574           contextManager.fireStateChanged(contextManager.getContext(), EVENT_REFRESH_VIEWS_ONLY);
575         }
576       });
577     }
578
579     public void threadStopped(DebugProcess proc, ThreadReference thread) {
580       ((VirtualMachineProxyImpl)proc.getVirtualMachineProxy()).threadStopped(thread);
581       DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() {
582         public void run() {
583           final DebuggerStateManager contextManager = getContextManager();
584           contextManager.fireStateChanged(contextManager.getContext(), EVENT_REFRESH_VIEWS_ONLY);
585         }
586       });
587     }
588   }
589
590   private class MyEvaluationListener implements EvaluationListener {
591     public void evaluationStarted(SuspendContextImpl context) {
592       myIsEvaluating = true;
593     }
594
595     public void evaluationFinished(final SuspendContextImpl context) {
596       myIsEvaluating = false;
597       DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() {
598         public void run() {
599           if (context != getSuspendContext()) {
600             getContextManager().setState(DebuggerContextUtil.createDebuggerContext(DebuggerSession.this, context), STATE_PAUSED, EVENT_REFRESH, null);
601           }
602         }
603       });
604     }
605   }
606 }