| /* |
| * Copyright 2000-2009 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /* |
| * @author Eugene Zhuravlev |
| */ |
| package com.intellij.debugger.jdi; |
| |
| import com.intellij.debugger.engine.DebuggerManagerThreadImpl; |
| import com.intellij.debugger.engine.evaluation.EvaluateException; |
| import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil; |
| import com.intellij.debugger.engine.jdi.ThreadReferenceProxy; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.sun.jdi.*; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| |
| import java.util.*; |
| |
| public final class ThreadReferenceProxyImpl extends ObjectReferenceProxyImpl implements ThreadReferenceProxy { |
| private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.jdi.ThreadReferenceProxyImpl"); |
| // cached data |
| private String myName; |
| private int myFrameCount = -1; |
| // stack frames, 0 - bottom |
| private final List<StackFrameProxyImpl> myFramesFromBottom = new ArrayList<StackFrameProxyImpl>(); |
| //cache build on the base of myFramesFromBottom 0 - top, initially nothing is cached |
| private List<StackFrameProxyImpl> myFrames = null; |
| |
| private ThreadGroupReferenceProxyImpl myThreadGroupProxy; |
| |
| public static final Comparator<ThreadReferenceProxyImpl> ourComparator = new Comparator<ThreadReferenceProxyImpl>() { |
| @Override |
| public int compare(ThreadReferenceProxyImpl th1, ThreadReferenceProxyImpl th2) { |
| return th1.name().compareToIgnoreCase(th2.name()); |
| } |
| }; |
| |
| public ThreadReferenceProxyImpl(VirtualMachineProxyImpl virtualMachineProxy, ThreadReference threadReference) { |
| super(virtualMachineProxy, threadReference); |
| } |
| |
| @Override |
| public ThreadReference getThreadReference() { |
| DebuggerManagerThreadImpl.assertIsManagerThread(); |
| return (ThreadReference)getObjectReference(); |
| } |
| |
| @Override |
| public VirtualMachineProxyImpl getVirtualMachine() { |
| DebuggerManagerThreadImpl.assertIsManagerThread(); |
| return (VirtualMachineProxyImpl) myTimer; |
| } |
| |
| public String name() { |
| checkValid(); |
| if (myName == null) { |
| try { |
| myName = getThreadReference().name(); |
| } |
| catch (ObjectCollectedException ignored) { |
| myName = ""; |
| } |
| catch (IllegalThreadStateException ignored) { |
| myName = "zombie"; |
| } |
| } |
| return myName; |
| } |
| |
| public int getSuspendCount() { |
| DebuggerManagerThreadImpl.assertIsManagerThread(); |
| //LOG.assertTrue((mySuspendCount > 0) == suspends()); |
| try { |
| return getThreadReference().suspendCount(); |
| } |
| catch (ObjectCollectedException ignored) { |
| return 0; |
| } |
| } |
| |
| public void suspend() { |
| DebuggerManagerThreadImpl.assertIsManagerThread(); |
| try { |
| getThreadReference().suspend(); |
| } |
| catch (ObjectCollectedException ignored) { |
| } |
| clearCaches(); |
| } |
| |
| @NonNls |
| public String toString() { |
| //noinspection HardCodedStringLiteral |
| @NonNls String threadRefString; |
| try { |
| threadRefString = getThreadReference().toString() ; |
| } |
| catch (ObjectCollectedException ignored) { |
| threadRefString = "[thread collected]"; |
| } |
| return "ThreadReferenceProxyImpl: " + threadRefString + " " + super.toString(); |
| } |
| |
| public void resume() { |
| DebuggerManagerThreadImpl.assertIsManagerThread(); |
| //JDI clears all caches on thread resume !! |
| final ThreadReference threadRef = getThreadReference(); |
| if(LOG.isDebugEnabled()) { |
| LOG.debug("before resume" + threadRef); |
| } |
| getVirtualMachineProxy().clearCaches(); |
| try { |
| threadRef.resume(); |
| } |
| catch (ObjectCollectedException ignored) { |
| } |
| } |
| |
| @Override |
| protected void clearCaches() { |
| DebuggerManagerThreadImpl.assertIsManagerThread(); |
| myName = null; |
| myFrames = null; |
| myFrameCount = -1; |
| super.clearCaches(); |
| } |
| |
| public int status() { |
| try { |
| return getThreadReference().status(); |
| } |
| catch (ObjectCollectedException ignored) { |
| return ThreadReference.THREAD_STATUS_ZOMBIE; |
| } |
| } |
| |
| public ThreadGroupReferenceProxyImpl threadGroupProxy() { |
| DebuggerManagerThreadImpl.assertIsManagerThread(); |
| checkValid(); |
| if(myThreadGroupProxy == null) { |
| ThreadGroupReference threadGroupRef; |
| try { |
| threadGroupRef = getThreadReference().threadGroup(); |
| } |
| catch (ObjectCollectedException ignored) { |
| threadGroupRef = null; |
| } |
| myThreadGroupProxy = getVirtualMachineProxy().getThreadGroupReferenceProxy(threadGroupRef); |
| } |
| return myThreadGroupProxy; |
| } |
| |
| @Override |
| public int frameCount() throws EvaluateException { |
| DebuggerManagerThreadImpl.assertIsManagerThread(); |
| checkValid(); |
| if (myFrameCount == -1) { |
| final ThreadReference threadReference = getThreadReference(); |
| try { |
| myFrameCount = threadReference.frameCount(); |
| } |
| catch(ObjectCollectedException ignored) { |
| myFrameCount = 0; |
| } |
| catch (IncompatibleThreadStateException e) { |
| final boolean isSuspended; |
| try { |
| isSuspended = threadReference.isSuspended(); |
| } |
| catch (Throwable ignored) { |
| // unable to determine whether the thread is actually suspended, so propagating original exception |
| throw EvaluateExceptionUtil.createEvaluateException(e); |
| } |
| if (!isSuspended) { |
| // give up because it seems to be really resumed |
| throw EvaluateExceptionUtil.createEvaluateException(e); |
| } |
| else { |
| // JDI bug: although isSuspended() == true, frameCount() may throw IncompatibleThreadStateException |
| // see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4783403 |
| // unfortunately, impossible to get this information at the moment, so assume the frame count is null |
| myFrameCount = 0; |
| } |
| } |
| } |
| return myFrameCount; |
| } |
| |
| public List<StackFrameProxyImpl> frames() throws EvaluateException { |
| DebuggerManagerThreadImpl.assertIsManagerThread(); |
| final ThreadReference threadRef = getThreadReference(); |
| try { |
| //LOG.assertTrue(threadRef.isSuspended()); |
| checkValid(); |
| |
| if(myFrames == null) { |
| checkFrames(threadRef); |
| |
| myFrames = new ArrayList<StackFrameProxyImpl>(frameCount()); |
| for (ListIterator<StackFrameProxyImpl> iterator = myFramesFromBottom.listIterator(frameCount()); iterator.hasPrevious();) { |
| myFrames.add(iterator.previous()); |
| } |
| } |
| } |
| catch (ObjectCollectedException ignored) { |
| return Collections.emptyList(); |
| } |
| return myFrames; |
| } |
| |
| private void checkFrames(@NotNull final ThreadReference threadRef) throws EvaluateException { |
| if (myFramesFromBottom.size() < frameCount()) { |
| int count = frameCount(); |
| List<StackFrame> frames; |
| try { |
| frames = threadRef.frames(0, count - myFramesFromBottom.size()); |
| } |
| catch (IncompatibleThreadStateException e) { |
| throw EvaluateExceptionUtil.createEvaluateException(e); |
| } |
| catch (InternalException e) { |
| throw EvaluateExceptionUtil.createEvaluateException(e); |
| } |
| |
| int index = myFramesFromBottom.size() + 1; |
| for (ListIterator<StackFrame> iterator = frames.listIterator(count - myFramesFromBottom.size()); iterator.hasPrevious();) { |
| myFramesFromBottom.add(new StackFrameProxyImpl(this, iterator.previous(), index)); |
| index++; |
| } |
| } |
| } |
| |
| @Override |
| public StackFrameProxyImpl frame(int i) throws EvaluateException { |
| DebuggerManagerThreadImpl.assertIsManagerThread(); |
| final ThreadReference threadReference = getThreadReference(); |
| try { |
| if(!threadReference.isSuspended()) { |
| return null; |
| } |
| checkFrames(threadReference); |
| final int frameCount = frameCount(); |
| if (frameCount == 0) { |
| return null; |
| } |
| return myFramesFromBottom.get(frameCount - i - 1); |
| } |
| catch (ObjectCollectedException ignored) { |
| return null; |
| } |
| catch (IllegalThreadStateException ignored) { |
| return null; |
| } |
| } |
| |
| public void popFrames(StackFrameProxyImpl stackFrame) throws EvaluateException { |
| DebuggerManagerThreadImpl.assertIsManagerThread(); |
| try { |
| getThreadReference().popFrames(stackFrame.getStackFrame()); |
| } |
| catch (InvalidStackFrameException ignored) { |
| } |
| catch (ObjectCollectedException ignored) { |
| } |
| catch (InternalException e) { |
| throw EvaluateExceptionUtil.createEvaluateException(e); |
| } |
| catch (IncompatibleThreadStateException e) { |
| throw EvaluateExceptionUtil.createEvaluateException(e); |
| } |
| finally { |
| clearCaches(); |
| getVirtualMachineProxy().clearCaches(); |
| } |
| } |
| |
| public boolean isSuspended() throws ObjectCollectedException { |
| try { |
| DebuggerManagerThreadImpl.assertIsManagerThread(); |
| return getThreadReference().isSuspended(); |
| } |
| catch (IllegalThreadStateException e) { |
| // must be zombie thread |
| LOG.info(e); |
| return false; |
| } |
| } |
| } |