From 190b8fa7728ef9af0e94246aae10095ce9908499 Mon Sep 17 00:00:00 2001 From: "Egor.Ushakov" Date: Thu, 24 Sep 2015 20:52:16 +0300 Subject: [PATCH] IDEA-63378 Switching threads should be optional, not forced - correctly show stepping threads in running state --- .../debugger/engine/DebugProcessEvents.java | 1 + .../debugger/engine/JavaDebugProcess.java | 20 ++++++- .../debugger/engine/JavaExecutionStack.java | 5 +- .../debugger/engine/JavaStackFrame.java | 2 +- .../debugger/engine/SuspendContextImpl.java | 14 +++-- .../debugger/engine/SuspendManagerImpl.java | 9 +-- .../debugger/impl/DebuggerContextImpl.java | 1 - .../debugger/impl/DebuggerSession.java | 58 ++++++++++++------- .../debugger/impl/ReloadClassesWorker.java | 2 +- .../debugger/jdi/StackFrameProxyImpl.java | 1 + .../xdebugger/impl/XDebugSessionImpl.java | 4 ++ .../src/messages/DebuggerBundle.properties | 1 + 12 files changed, 76 insertions(+), 42 deletions(-) diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessEvents.java b/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessEvents.java index 701eec904a61..df4c905f42c2 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessEvents.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessEvents.java @@ -416,6 +416,7 @@ public class DebugProcessEvents extends DebugProcessImpl { final int nextStepDepth = hint.getNextStepDepth(suspendContext); if (nextStepDepth == RequestHint.RESUME) { getSession().resetIgnoreStepFiltersFlag(); + getSession().clearSteppingThrough(); shouldResume = true; } else if (nextStepDepth != RequestHint.STOP) { diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/JavaDebugProcess.java b/java/debugger/impl/src/com/intellij/debugger/engine/JavaDebugProcess.java index 9b9a571463e3..1b499e146afe 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/JavaDebugProcess.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/JavaDebugProcess.java @@ -40,6 +40,7 @@ import com.intellij.icons.AllIcons; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; @@ -106,7 +107,7 @@ public class JavaDebugProcess extends XDebugProcess { || event == DebuggerSession.Event.REFRESH && myJavaSession.isPaused()) { final SuspendContextImpl newSuspendContext = newContext.getSuspendContext(); - if (newSuspendContext != null && newSuspendContext != getSession().getSuspendContext()) { + if (newSuspendContext != null && shouldApplyContext(newContext)) { process.getManagerThread().schedule(new SuspendContextCommandImpl(newSuspendContext) { @Override public void contextAction() throws Exception { @@ -119,10 +120,12 @@ public class JavaDebugProcess extends XDebugProcess { XBreakpoint xBreakpoint = breakpoint.getXBreakpoint(); if (xBreakpoint != null) { ((XDebugSessionImpl)getSession()).breakpointReachedNoProcessing(xBreakpoint, newSuspendContext); + unsetPausedIfNeeded(newContext); return; } } getSession().positionReached(newSuspendContext); + unsetPausedIfNeeded(newContext); } }); } @@ -181,6 +184,21 @@ public class JavaDebugProcess extends XDebugProcess { }); } + private void unsetPausedIfNeeded(DebuggerContextImpl context) { + SuspendContextImpl suspendContext = context.getSuspendContext(); + if (suspendContext != null && context.getThreadProxy() != suspendContext.getThread()) { + ((XDebugSessionImpl)getSession()).unsetPaused(); + } + } + + private boolean shouldApplyContext(DebuggerContextImpl context) { + SuspendContextImpl suspendContext = context.getSuspendContext(); + SuspendContextImpl currentContext = (SuspendContextImpl)getSession().getSuspendContext(); + if (suspendContext != null && !suspendContext.equals(currentContext)) return true; + JavaExecutionStack currentExecutionStack = currentContext != null ? currentContext.getActiveExecutionStack() : null; + return currentExecutionStack == null || !Comparing.equal(context.getThreadProxy(), currentExecutionStack.getThreadProxy()); + } + public void saveNodeHistory() { saveNodeHistory(getDebuggerStateManager().getContext().getFrameProxy()); } diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/JavaExecutionStack.java b/java/debugger/impl/src/com/intellij/debugger/engine/JavaExecutionStack.java index 4f234ad2fbcc..b0d8d3e6539d 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/JavaExecutionStack.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/JavaExecutionStack.java @@ -48,14 +48,11 @@ public class JavaExecutionStack extends XExecutionStack { super(calcRepresentation(threadProxy), calcIcon(threadProxy, current)); myThreadProxy = threadProxy; myDebugProcess = debugProcess; - if (current) { - initTopFrame(); - } } private static Icon calcIcon(ThreadReferenceProxyImpl threadProxy, boolean current) { if (current) { - return AllIcons.Debugger.ThreadCurrent; + return threadProxy.isSuspended() ? AllIcons.Debugger.ThreadCurrent : AllIcons.Debugger.ThreadRunning; } else if (threadProxy.isAtBreakpoint()) { return AllIcons.Debugger.ThreadAtBreakpoint; diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/JavaStackFrame.java b/java/debugger/impl/src/com/intellij/debugger/engine/JavaStackFrame.java index 3a8c2b6bac48..8d08444e9b86 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/JavaStackFrame.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/JavaStackFrame.java @@ -138,7 +138,7 @@ public class JavaStackFrame extends XStackFrame { xFrame.computeChildren(node); return; } - myDebugProcess.getManagerThread().schedule(new DebuggerContextCommandImpl(myDebugProcess.getDebuggerContext()) { + myDebugProcess.getManagerThread().schedule(new DebuggerContextCommandImpl(myDebugProcess.getDebuggerContext(), myDescriptor.getFrameProxy().threadProxy()) { @Override public Priority getPriority() { return Priority.NORMAL; diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/SuspendContextImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/SuspendContextImpl.java index 2a4dd9de6156..e0cacfe51ccd 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/SuspendContextImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/SuspendContextImpl.java @@ -26,7 +26,6 @@ import com.intellij.debugger.jdi.ThreadReferenceProxyImpl; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Comparing; import com.intellij.util.containers.HashSet; -import com.intellij.xdebugger.frame.XExecutionStack; import com.intellij.xdebugger.frame.XSuspendContext; import com.sun.jdi.ObjectReference; import com.sun.jdi.ThreadReference; @@ -229,15 +228,18 @@ public abstract class SuspendContextImpl extends XSuspendContext implements Susp @Nullable @Override - public XExecutionStack getActiveExecutionStack() { + public JavaExecutionStack getActiveExecutionStack() { return myActiveExecutionStack; } - public void initExecutionStacks(ThreadReferenceProxyImpl newThread) { + public void initExecutionStacks(ThreadReferenceProxyImpl activeThread) { DebuggerManagerThreadImpl.assertIsManagerThread(); - myThread = newThread; - if (newThread != null) { - myActiveExecutionStack = new JavaExecutionStack(newThread, myDebugProcess, true); + if (myThread == null) { + myThread = activeThread; + } + if (activeThread != null) { + myActiveExecutionStack = new JavaExecutionStack(activeThread, myDebugProcess, myThread == activeThread); + myActiveExecutionStack.initTopFrame(); } } diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/SuspendManagerImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/SuspendManagerImpl.java index 2edc16ff6953..0fe1e45cc5dc 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/SuspendManagerImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/SuspendManagerImpl.java @@ -115,13 +115,6 @@ public class SuspendManagerImpl implements SuspendManager { return suspendContext; } - public SuspendContextImpl createDummyContext(@MagicConstant(flagsFromClass = EventRequest.class) int suspendPolicy) { - return new SuspendContextImpl(myDebugProcess, suspendPolicy, 0, null) { - @Override - protected void resumeImpl() {} - }; - } - @Override public SuspendContextImpl pushSuspendContext(final EventSet set) { SuspendContextImpl suspendContext = new SuspendContextImpl(myDebugProcess, set.suspendPolicy(), set.size(), set) { @@ -360,7 +353,7 @@ public class SuspendManagerImpl implements SuspendManager { processVote(suspendContext); } - LinkedList getPausedContexts() { + public List getPausedContexts() { return myPausedContexts; } } diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerContextImpl.java b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerContextImpl.java index ad9e01e26409..336871008856 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerContextImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerContextImpl.java @@ -151,7 +151,6 @@ public final class DebuggerContextImpl implements DebuggerContext { ThreadReferenceProxyImpl threadProxy, StackFrameProxyImpl frameProxy) { LOG.assertTrue(frameProxy == null || threadProxy == null || threadProxy == frameProxy.threadProxy()); - LOG.assertTrue(session == null || session.getProcess() != null); return new DebuggerContextImpl(session, session != null ? session.getProcess() : null, context, threadProxy, frameProxy, null, null, context == null); } diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerSession.java b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerSession.java index 57f2bbc092a0..b33789a5bc6c 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerSession.java +++ b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerSession.java @@ -111,7 +111,7 @@ public class DebuggerSession implements AbstractDebuggerSession { mySteppingThroughThread.set(threadProxy); } - void clearSteppingThrough() { + public void clearSteppingThrough() { mySteppingThroughThread.set(null); } @@ -469,6 +469,8 @@ public class DebuggerSession implements AbstractDebuggerSession { LOG.debug("paused"); } + ThreadReferenceProxyImpl currentThread = suspendContext.getThread(); + if (!shouldSetAsActiveContext(suspendContext)) { DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() { @Override @@ -476,7 +478,7 @@ public class DebuggerSession implements AbstractDebuggerSession { getContextManager().fireStateChanged(getContextManager().getContext(), Event.THREADS_REFRESH); } }); - final ThreadReferenceProxyImpl thread = suspendContext.getThread(); + ThreadReferenceProxyImpl thread = suspendContext.getThread(); if (thread != null) { List> descriptors = DebuggerUtilsEx.getEventDescriptors(suspendContext); if (!descriptors.isEmpty()) { @@ -492,7 +494,7 @@ public class DebuggerSession implements AbstractDebuggerSession { @Override public void contextAction() throws Exception { final DebuggerContextImpl debuggerContext = - DebuggerContextImpl.createDebuggerContext(DebuggerSession.this, suspendContext, thread, null); + DebuggerContextUtil.createDebuggerContext(DebuggerSession.this, suspendContext); DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() { @Override @@ -507,12 +509,17 @@ public class DebuggerSession implements AbstractDebuggerSession { }).notify(getProject()); } } - return; + if (((SuspendManagerImpl)myDebugProcess.getSuspendManager()).getPausedContexts().size() > 1) { + return; + } + else { + currentThread = mySteppingThroughThread.get(); + } + } + else { + setSteppingThrough(currentThread); } - setSteppingThrough(suspendContext.getThread()); - - ThreadReferenceProxyImpl currentThread = suspendContext.getThread(); final StackFrameContext positionContext; if (currentThread == null) { @@ -626,7 +633,7 @@ public class DebuggerSession implements AbstractDebuggerSession { DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() { @Override public void run() { - getContextManager().setState(debuggerContext, State.PAUSED, Event.PAUSE, null); + getContextManager().setState(debuggerContext, State.PAUSED, Event.PAUSE, getDescription(debuggerContext)); } }); } @@ -650,25 +657,28 @@ public class DebuggerSession implements AbstractDebuggerSession { @Override public void resumed(SuspendContextImpl suspendContext) { - SuspendManager suspendManager = getProcess().getSuspendManager(); - SuspendContextImpl context = suspendManager.getPausedContext(); + SuspendContextImpl context = getProcess().getSuspendManager().getPausedContext(); + ThreadReferenceProxyImpl steppingThread = null; // single thread stepping - if (suspendContext != null + if (context != null + && suspendContext != null && suspendContext.getSuspendPolicy() == EventRequest.SUSPEND_EVENT_THREAD && isSteppingThrough(suspendContext.getThread())) { - ThreadReferenceProxyImpl thread = suspendContext.getThread(); - if (context == null || !Comparing.equal(context.getThread(), thread)) { - context = ((SuspendManagerImpl)suspendManager).createDummyContext(suspendContext.getSuspendPolicy()); - context.setThread(thread.getThreadReference()); - } + steppingThread = suspendContext.getThread(); } - final SuspendContextImpl currentContext = context; + final DebuggerContextImpl debuggerContext = + context != null ? + DebuggerContextImpl.createDebuggerContext(DebuggerSession.this, + context, + steppingThread != null ? steppingThread : context.getThread(), + null) + : null; + DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() { @Override public void run() { - if (currentContext != null) { - getContextManager().setState(DebuggerContextUtil.createDebuggerContext(DebuggerSession.this, currentContext), - State.PAUSED, Event.CONTEXT, null); + if (debuggerContext != null) { + getContextManager().setState(debuggerContext, State.PAUSED, Event.CONTEXT, getDescription(debuggerContext)); } else { getContextManager().setState(SESSION_EMPTY_CONTEXT, State.RUNNING, Event.CONTEXT, null); @@ -756,6 +766,14 @@ public class DebuggerSession implements AbstractDebuggerSession { } } + private static String getDescription(DebuggerContextImpl debuggerContext) { + SuspendContextImpl suspendContext = debuggerContext.getSuspendContext(); + if (suspendContext != null && debuggerContext.getThreadProxy() != suspendContext.getThread()) { + return DebuggerBundle.message("status.paused.in.another.thread"); + } + return null; + } + private class MyEvaluationListener implements EvaluationListener { @Override public void evaluationStarted(SuspendContextImpl context) { diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/ReloadClassesWorker.java b/java/debugger/impl/src/com/intellij/debugger/impl/ReloadClassesWorker.java index 7b3f46e27c8e..6693150f03aa 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/ReloadClassesWorker.java +++ b/java/debugger/impl/src/com/intellij/debugger/impl/ReloadClassesWorker.java @@ -176,7 +176,7 @@ class ReloadClassesWorker { SuspendContextImpl suspendContext = context.getSuspendContext(); if (suspendContext != null) { XExecutionStack stack = suspendContext.getActiveExecutionStack(); - if (stack instanceof JavaExecutionStack) { + if (stack != null) { ((JavaExecutionStack)stack).initTopFrame(); } } diff --git a/java/debugger/impl/src/com/intellij/debugger/jdi/StackFrameProxyImpl.java b/java/debugger/impl/src/com/intellij/debugger/jdi/StackFrameProxyImpl.java index 957ada3482a2..c75a41f1f613 100644 --- a/java/debugger/impl/src/com/intellij/debugger/jdi/StackFrameProxyImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/jdi/StackFrameProxyImpl.java @@ -193,6 +193,7 @@ public class StackFrameProxyImpl extends JdiProxy implements StackFrameProxy { throw new EvaluateException(error.getMessage(), error); } + @NotNull @Override public ThreadReferenceProxyImpl threadProxy() { return myThreadProxy; diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebugSessionImpl.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebugSessionImpl.java index 91c788c742c8..40359b2a6dd4 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebugSessionImpl.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebugSessionImpl.java @@ -814,6 +814,10 @@ public class XDebugSessionImpl implements XDebugSession { }); } + public void unsetPaused() { + myPaused.set(false); + } + @Override public void positionReached(@NotNull final XSuspendContext suspendContext) { enableBreakpoints(); diff --git a/resources-en/src/messages/DebuggerBundle.properties b/resources-en/src/messages/DebuggerBundle.properties index 8c24513cc9d7..3eb9637f6ca6 100644 --- a/resources-en/src/messages/DebuggerBundle.properties +++ b/resources-en/src/messages/DebuggerBundle.properties @@ -91,6 +91,7 @@ status.step.into=Stepping into status.step.over=Stepping over status.run.to.cursor=Run to cursor status.process.resumed=Process resumed +status.paused.in.another.thread=Paused in another thread error.pop.bottom.stackframe=Cannot pop bottom frame error.pop.stackframe=An error occurred while popping stack frame: {0} error.class.not.loaded=Class not loaded : {0}