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