sort suspended threads on top
[idea/community.git] / java / debugger / impl / src / com / intellij / debugger / jdi / ThreadReferenceProxyImpl.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
17 /*
18  * @author Eugene Zhuravlev
19  */
20 package com.intellij.debugger.jdi;
21
22 import com.intellij.debugger.DebuggerBundle;
23 import com.intellij.debugger.engine.DebuggerManagerThreadImpl;
24 import com.intellij.debugger.engine.evaluation.EvaluateException;
25 import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
26 import com.intellij.debugger.engine.jdi.ThreadReferenceProxy;
27 import com.intellij.openapi.diagnostic.Logger;
28 import com.intellij.openapi.util.Comparing;
29 import com.sun.jdi.*;
30 import org.jetbrains.annotations.NonNls;
31 import org.jetbrains.annotations.NotNull;
32
33 import java.util.*;
34
35 public final class ThreadReferenceProxyImpl extends ObjectReferenceProxyImpl implements ThreadReferenceProxy {
36   private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.jdi.ThreadReferenceProxyImpl");
37   // cached data
38   private String myName;
39   private int                       myFrameCount = -1;
40   // stack frames, 0 - bottom
41   private final List<StackFrameProxyImpl> myFramesFromBottom = new ArrayList<StackFrameProxyImpl>();
42   //cache build on the base of myFramesFromBottom 0 - top, initially nothing is cached
43   private List<StackFrameProxyImpl> myFrames = null;
44
45   private ThreadGroupReferenceProxyImpl myThreadGroupProxy;
46
47   public static final Comparator<ThreadReferenceProxyImpl> ourComparator = new Comparator<ThreadReferenceProxyImpl>() {
48     @Override
49     public int compare(ThreadReferenceProxyImpl th1, ThreadReferenceProxyImpl th2) {
50       int res = Comparing.compare(th2.isSuspended(), th1.isSuspended());
51       if (res == 0) {
52         return th1.name().compareToIgnoreCase(th2.name());
53       }
54       return res;
55     }
56   };
57
58   public ThreadReferenceProxyImpl(VirtualMachineProxyImpl virtualMachineProxy, ThreadReference threadReference) {
59     super(virtualMachineProxy, threadReference);
60   }
61
62   @Override
63   public ThreadReference getThreadReference() {
64     DebuggerManagerThreadImpl.assertIsManagerThread();
65     return (ThreadReference)getObjectReference();
66   }
67
68   @Override
69   public VirtualMachineProxyImpl getVirtualMachine() {
70     DebuggerManagerThreadImpl.assertIsManagerThread();
71     return (VirtualMachineProxyImpl) myTimer;
72   }
73
74   public String name() {
75     checkValid();
76     if (myName == null) {
77       try {
78         myName = getThreadReference().name();
79       }
80       catch (ObjectCollectedException ignored) {
81         myName = "";
82       }
83       catch (IllegalThreadStateException ignored) {
84         myName = "zombie";
85       }                    
86     }
87     return myName;
88   }
89
90   public int getSuspendCount() {
91     DebuggerManagerThreadImpl.assertIsManagerThread();
92     //LOG.assertTrue((mySuspendCount > 0) == suspends());
93     try {
94       return getThreadReference().suspendCount();
95     }
96     catch (ObjectCollectedException ignored) {
97       return 0;
98     }
99   }
100
101   public void suspend() {
102     DebuggerManagerThreadImpl.assertIsManagerThread();
103     try {
104       getThreadReference().suspend();
105     }
106     catch (ObjectCollectedException ignored) {
107     }
108     clearCaches();
109   }
110
111   @NonNls
112   public String toString() {
113     //noinspection HardCodedStringLiteral
114     @NonNls String threadRefString;
115     try {
116       threadRefString = getThreadReference().toString() ;
117     }
118     catch (ObjectCollectedException ignored) {
119       threadRefString = "[thread collected]";
120     }
121     return "ThreadReferenceProxyImpl: " + threadRefString + " " + super.toString();
122   }
123
124   public void resume() {
125     DebuggerManagerThreadImpl.assertIsManagerThread();
126     //JDI clears all caches on thread resume !!
127     final ThreadReference threadRef = getThreadReference();
128     if(LOG.isDebugEnabled()) {
129       LOG.debug("before resume" + threadRef);
130     }
131     getVirtualMachineProxy().clearCaches();
132     try {
133       threadRef.resume();
134     }
135     catch (ObjectCollectedException ignored) {
136     }
137   }
138
139   @Override
140   protected void clearCaches() {
141     DebuggerManagerThreadImpl.assertIsManagerThread();
142     myName = null;
143     myFrames = null;
144     myFrameCount = -1;
145     super.clearCaches();
146   }
147
148   public int status() {
149     try {
150       return getThreadReference().status();
151     }
152     catch (IllegalThreadStateException e) {
153       return ThreadReference.THREAD_STATUS_ZOMBIE;
154     }
155     catch (ObjectCollectedException ignored) {
156       return ThreadReference.THREAD_STATUS_ZOMBIE;
157     }
158   }
159
160   public ThreadGroupReferenceProxyImpl threadGroupProxy() {
161     DebuggerManagerThreadImpl.assertIsManagerThread();
162     checkValid();
163     if(myThreadGroupProxy == null) {
164       ThreadGroupReference threadGroupRef;
165       try {
166         threadGroupRef = getThreadReference().threadGroup();
167       }
168       catch (ObjectCollectedException ignored) {
169         threadGroupRef = null;
170       }
171       myThreadGroupProxy = getVirtualMachineProxy().getThreadGroupReferenceProxy(threadGroupRef);
172     }
173     return myThreadGroupProxy;
174   }
175
176   @Override
177   public int frameCount() throws EvaluateException {
178     DebuggerManagerThreadImpl.assertIsManagerThread();
179     checkValid();
180     if (myFrameCount == -1) {
181       final ThreadReference threadReference = getThreadReference();
182       try {
183         myFrameCount = threadReference.frameCount();
184       }
185       catch(ObjectCollectedException ignored) {
186         myFrameCount = 0;
187       }
188       catch (IncompatibleThreadStateException e) {
189         final boolean isSuspended;
190         try {
191           isSuspended = threadReference.isSuspended();
192         }
193         catch (Throwable ignored) {
194           // unable to determine whether the thread is actually suspended, so propagating original exception
195           throw EvaluateExceptionUtil.createEvaluateException(e);
196         }
197         if (!isSuspended) {
198           // give up because it seems to be really resumed
199           throw EvaluateExceptionUtil.createEvaluateException(e);
200         }
201         else {
202           // JDI bug: although isSuspended() == true, frameCount() may throw IncompatibleThreadStateException
203           // see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4783403
204           // unfortunately, impossible to get this information at the moment, so assume the frame count is null
205           myFrameCount = 0;
206         }
207       }
208       catch (InternalException e) {
209         LOG.info(e);
210         myFrameCount = 0;
211       }
212     }
213     return myFrameCount;
214   }
215
216   public List<StackFrameProxyImpl> frames() throws EvaluateException {
217     DebuggerManagerThreadImpl.assertIsManagerThread();
218     final ThreadReference threadRef = getThreadReference();
219     try {
220       //LOG.assertTrue(threadRef.isSuspended());
221       checkValid();
222
223       if(myFrames == null) {
224         checkFrames(threadRef);
225   
226         myFrames = new ArrayList<StackFrameProxyImpl>(frameCount());
227         for (ListIterator<StackFrameProxyImpl> iterator = myFramesFromBottom.listIterator(frameCount()); iterator.hasPrevious();) {
228           myFrames.add(iterator.previous());
229         }
230       }
231     }
232     catch (ObjectCollectedException ignored) {
233       return Collections.emptyList();
234     }
235     return myFrames;
236   }
237
238   private void checkFrames(@NotNull final ThreadReference threadRef) throws EvaluateException {
239     if (myFramesFromBottom.size() < frameCount()) {
240       int count = frameCount();
241       List<StackFrame> frames;
242       try {
243         frames = threadRef.frames(0, count - myFramesFromBottom.size());
244       }
245       catch (IncompatibleThreadStateException e) {
246         throw EvaluateExceptionUtil.createEvaluateException(e);
247       }
248       catch (InternalException e) {
249         throw EvaluateExceptionUtil.createEvaluateException(e);
250       }
251
252       int index = myFramesFromBottom.size() + 1;
253       for (ListIterator<StackFrame> iterator = frames.listIterator(count - myFramesFromBottom.size()); iterator.hasPrevious();) {
254         myFramesFromBottom.add(new StackFrameProxyImpl(this, iterator.previous(), index));
255         index++;
256       }
257     }
258   }
259
260   @Override
261   public StackFrameProxyImpl frame(int i) throws EvaluateException {
262     DebuggerManagerThreadImpl.assertIsManagerThread();
263     final ThreadReference threadReference = getThreadReference();
264     try {
265       if(!threadReference.isSuspended()) {
266         return null;
267       }
268       checkFrames(threadReference);
269       final int frameCount = frameCount();
270       if (frameCount == 0) {
271         return null;
272       }
273       return myFramesFromBottom.get(frameCount - i  - 1);
274     }
275     catch (ObjectCollectedException ignored) {
276       return null;
277     }
278     catch (IllegalThreadStateException ignored) {
279       return null;
280     }
281   }
282
283   public void popFrames(StackFrameProxyImpl stackFrame) throws EvaluateException {
284     DebuggerManagerThreadImpl.assertIsManagerThread();
285     try {
286       getThreadReference().popFrames(stackFrame.getStackFrame());
287     }
288     catch (InvalidStackFrameException ignored) {
289     }
290     catch (ObjectCollectedException ignored) {
291     }
292     catch (InternalException e) {
293       if (e.errorCode() == 32) {
294         throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("drop.frame.error.no.information"));
295       }
296       else throw EvaluateExceptionUtil.createEvaluateException(e);
297     }
298     catch (IncompatibleThreadStateException e) {
299       throw EvaluateExceptionUtil.createEvaluateException(e);
300     }
301     finally {
302       clearCaches();
303       getVirtualMachineProxy().clearCaches();
304     }
305   }
306
307   public boolean isSuspended() throws ObjectCollectedException {
308     try {
309       DebuggerManagerThreadImpl.assertIsManagerThread();
310       return getThreadReference().isSuspended();
311     }
312     catch (IllegalThreadStateException e) {
313       // must be zombie thread
314       LOG.info(e);
315       return false;
316     }
317   }
318
319   public boolean isAtBreakpoint() {
320     try {
321       return getThreadReference().isAtBreakpoint();
322     } catch (InternalException e) {
323       LOG.info(e);
324     }
325     return false;
326   }
327 }