blob: 1bd95096055f5c97e9cfd0a3af187eaef3626002 [file] [log] [blame]
/*
* Copyright 2000-2014 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.
*/
package com.intellij.debugger.engine;
import com.intellij.debugger.DebuggerBundle;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.events.DebuggerContextCommandImpl;
import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
import com.intellij.debugger.impl.DebuggerUtilsEx;
import com.intellij.debugger.jdi.StackFrameProxyImpl;
import com.intellij.debugger.jdi.ThreadGroupReferenceProxyImpl;
import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
import com.intellij.debugger.settings.DebuggerSettings;
import com.intellij.debugger.ui.impl.watch.MethodsTracker;
import com.intellij.debugger.ui.impl.watch.NodeManagerImpl;
import com.intellij.icons.AllIcons;
import com.intellij.xdebugger.frame.XExecutionStack;
import com.sun.jdi.ThreadReference;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
/**
* @author egor
*/
public class JavaExecutionStack extends XExecutionStack {
private final ThreadReferenceProxyImpl myThreadProxy;
private final DebugProcessImpl myDebugProcess;
private final NodeManagerImpl myNodeManager;
private volatile JavaStackFrame myTopFrame;
private volatile boolean myTopFrameReady = false;
private final MethodsTracker myTracker = new MethodsTracker();
public JavaExecutionStack(@NotNull ThreadReferenceProxyImpl threadProxy, @NotNull DebugProcessImpl debugProcess, boolean current) {
super(calcRepresentation(threadProxy), calcIcon(threadProxy, current));
myThreadProxy = threadProxy;
myDebugProcess = debugProcess;
myNodeManager = myDebugProcess.getXdebugProcess().getNodeManager();
if (current) {
myTopFrame = calcTopFrame();
}
}
private static Icon calcIcon(ThreadReferenceProxyImpl threadProxy, boolean current) {
if (current) {
return AllIcons.Debugger.ThreadCurrent;
}
else if (threadProxy.getThreadReference().isAtBreakpoint()) {
return AllIcons.Debugger.ThreadAtBreakpoint;
}
else if (threadProxy.isSuspended()) {
return AllIcons.Debugger.ThreadSuspended;
}
else {
return AllIcons.Debugger.ThreadRunning;
}
}
@NotNull
ThreadReferenceProxyImpl getThreadProxy() {
return myThreadProxy;
}
private JavaStackFrame calcTopFrame() {
DebuggerManagerThreadImpl.assertIsManagerThread();
try {
StackFrameProxyImpl frame = myThreadProxy.frame(0);
if (frame != null) {
return new JavaStackFrame(frame, myDebugProcess, myTracker, myNodeManager);
}
}
catch (EvaluateException e) {
e.printStackTrace();
}
finally {
myTopFrameReady = true;
}
return null;
}
@Nullable
@Override
public JavaStackFrame getTopFrame() {
assert myTopFrameReady : "Top frame must be already calculated here";
return myTopFrame;
}
@Override
public void computeStackFrames(final int firstFrameIndex, final XStackFrameContainer container) {
myDebugProcess.getManagerThread().schedule(new DebuggerContextCommandImpl(myDebugProcess.getDebuggerContext()) {
@Override
public Priority getPriority() {
return Priority.NORMAL;
}
@Override
public void threadAction() {
if (!myThreadProxy.isCollected() && myDebugProcess.getSuspendManager().isSuspended(myThreadProxy)) {
int status = myThreadProxy.status();
if (!(status == ThreadReference.THREAD_STATUS_UNKNOWN) &&
!(status == ThreadReference.THREAD_STATUS_NOT_STARTED) &&
!(status == ThreadReference.THREAD_STATUS_ZOMBIE)) {
try {
int added = 0;
Iterator<StackFrameProxyImpl> iterator = myThreadProxy.frames().iterator();
if (iterator.hasNext() && firstFrameIndex > 0) {
iterator.next();
added++;
}
myDebugProcess.getManagerThread().schedule(new AppendFrameCommand(getSuspendContext(), iterator, container, added, firstFrameIndex));
}
catch (EvaluateException e) {
container.errorOccurred(e.getMessage());
}
}
}
else {
container.errorOccurred(DebuggerBundle.message("frame.panel.frames.not.available"));
}
}
});
}
private class AppendFrameCommand extends SuspendContextCommandImpl {
private final Iterator<StackFrameProxyImpl> myStackFramesIterator;
private final XStackFrameContainer myContainer;
private int myAdded;
private final int mySkip;
public AppendFrameCommand(SuspendContextImpl suspendContext,
Iterator<StackFrameProxyImpl> stackFramesIterator,
XStackFrameContainer container,
int added,
int skip) {
super(suspendContext);
myStackFramesIterator = stackFramesIterator;
myContainer = container;
myAdded = added;
mySkip = skip;
}
@Override
public Priority getPriority() {
return myAdded <= 10 ? Priority.NORMAL : Priority.LOW;
}
@Override
public void contextAction() throws Exception {
if (myStackFramesIterator.hasNext()) {
JavaStackFrame frame;
boolean first = myAdded == 0;
if (first && myTopFrameReady) {
frame = myTopFrame;
myStackFramesIterator.next();
}
else {
frame = new JavaStackFrame(myStackFramesIterator.next(), myDebugProcess, myTracker, myNodeManager);
if (first && !myTopFrameReady) {
myTopFrame = frame;
myTopFrameReady = true;
}
}
if (first || DebuggerSettings.getInstance().SHOW_LIBRARY_STACKFRAMES || (!frame.getDescriptor().isSynthetic() && !frame.getDescriptor().isInLibraryContent())) {
if (++myAdded > mySkip) {
myContainer.addStackFrames(Arrays.asList(frame), false);
}
}
myDebugProcess.getManagerThread().schedule(
new AppendFrameCommand(getSuspendContext(), myStackFramesIterator, myContainer, myAdded, mySkip));
}
else {
myContainer.addStackFrames(Collections.<JavaStackFrame>emptyList(), true);
}
}
}
private static String calcRepresentation(ThreadReferenceProxyImpl thread) {
DebuggerManagerThreadImpl.assertIsManagerThread();
String name = thread.name();
ThreadGroupReferenceProxyImpl gr = thread.threadGroupProxy();
final String grname = (gr != null)? gr.name() : null;
final String threadStatusText = DebuggerUtilsEx.getThreadStatusText(thread.status());
//noinspection HardCodedStringLiteral
if (grname != null && !"SYSTEM".equalsIgnoreCase(grname)) {
return DebuggerBundle.message("label.thread.node.in.group", name, thread.uniqueID(), threadStatusText, grname);
}
return DebuggerBundle.message("label.thread.node", name, thread.uniqueID(), threadStatusText);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
JavaExecutionStack stack = (JavaExecutionStack)o;
if (!myThreadProxy.equals(stack.myThreadProxy)) return false;
return true;
}
@Override
public int hashCode() {
return myThreadProxy.hashCode();
}
}