From fb8ddd084b46b6aa43a77d68e7bca497deba8936 Mon Sep 17 00:00:00 2001 From: Eugene Zhuravlev Date: Tue, 7 Sep 2010 21:43:07 +0400 Subject: [PATCH] 1. InterruptThread action available from the ThreadsPane (IDEA-23341) 2. ThreadsPane shows running threads and updates their state even if debug VM is not paused 3. tests fixed --- .../actions/InterruptThreadAction.java | 98 +++++++++++++++++++ .../intellij/debugger/engine/ContextUtil.java | 3 + .../engine/DebugProcessAdapterImpl.java | 9 ++ .../debugger/engine/DebugProcessEvents.java | 51 ++++++++-- .../debugger/engine/SuspendManagerUtil.java | 3 + .../debugger/impl/DebuggerSession.java | 21 ++++ .../debugger/jdi/VirtualMachineProxyImpl.java | 18 +++- .../debugger/ui/DebuggerSessionTab.java | 8 +- .../debugger/ui/impl/FrameDebuggerTree.java | 3 +- .../debugger/ui/impl/ThreadsDebuggerTree.java | 52 ++++++---- .../debugger/ui/impl/ThreadsPanel.java | 93 +++++++++++++++++- .../ui/impl/UpdatableDebuggerView.java | 8 +- .../debugger/ui/impl/watch/DebuggerTree.java | 38 +++++-- .../ui/impl/watch/ThisDescriptorImpl.java | 2 +- .../ui/impl/watch/ThreadDescriptorImpl.java | 13 ++- .../impl/watch/ThreadGroupDescriptorImpl.java | 4 +- .../ui/impl/watch/ValueDescriptorImpl.java | 2 +- .../debugger/engine/DebugProcessAdapter.java | 9 ++ .../debugger/engine/DebugProcessListener.java | 5 + .../src/messages/ActionsBundle.properties | 1 + .../src/messages/DebuggerBundle.properties | 1 + resources/src/idea/IdeaActions.xml | 2 + 22 files changed, 382 insertions(+), 62 deletions(-) create mode 100644 java/debugger/impl/src/com/intellij/debugger/actions/InterruptThreadAction.java diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/InterruptThreadAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/InterruptThreadAction.java new file mode 100644 index 000000000000..62e861d246a6 --- /dev/null +++ b/java/debugger/impl/src/com/intellij/debugger/actions/InterruptThreadAction.java @@ -0,0 +1,98 @@ +/* + * Copyright 2000-2010 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.actions; + +import com.intellij.debugger.DebuggerBundle; +import com.intellij.debugger.engine.events.DebuggerCommandImpl; +import com.intellij.debugger.impl.DebuggerContextImpl; +import com.intellij.debugger.jdi.ThreadReferenceProxyImpl; +import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl; +import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl; +import com.intellij.debugger.ui.impl.watch.ThreadDescriptorImpl; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.Presentation; + +import java.util.ArrayList; +import java.util.List; + +/** + * User: lex + * Date: Sep 26, 2003 + * Time: 7:35:09 PM + */ +public class InterruptThreadAction extends DebuggerAction{ + + public void actionPerformed(final AnActionEvent e) { + final DebuggerTreeNodeImpl[] nodes = getSelectedNodes(e.getDataContext()); + if (nodes == null) { + return; + } + + //noinspection ConstantConditions + final List threadsToInterrupt = new ArrayList(); + for (final DebuggerTreeNodeImpl debuggerTreeNode : nodes) { + final NodeDescriptorImpl descriptor = debuggerTreeNode.getDescriptor(); + if (descriptor instanceof ThreadDescriptorImpl) { + threadsToInterrupt.add(((ThreadDescriptorImpl)descriptor).getThreadReference()); + } + } + + if (!threadsToInterrupt.isEmpty()) { + final DebuggerContextImpl debuggerContext = getDebuggerContext(e.getDataContext()); + debuggerContext.getDebugProcess().getManagerThread().schedule(new DebuggerCommandImpl() { + protected void action() throws Exception { + for (ThreadReferenceProxyImpl thread : threadsToInterrupt) { + thread.getThreadReference().interrupt(); + } + } + }); + } + + } + + public void update(AnActionEvent e) { + final DebuggerTreeNodeImpl[] selectedNodes = getSelectedNodes(e.getDataContext()); + + boolean visible = false; + boolean enabled = false; + + if(selectedNodes != null && selectedNodes.length > 0){ + visible = true; + enabled = true; + for (DebuggerTreeNodeImpl selectedNode : selectedNodes) { + final NodeDescriptorImpl threadDescriptor = selectedNode.getDescriptor(); + if (!(threadDescriptor instanceof ThreadDescriptorImpl)) { + visible = false; + break; + } + } + + if (visible) { + for (DebuggerTreeNodeImpl selectedNode : selectedNodes) { + final ThreadDescriptorImpl threadDescriptor = (ThreadDescriptorImpl)selectedNode.getDescriptor(); + if (threadDescriptor.isFrozen()) { + enabled = false; + break; + } + } + } + } + final Presentation presentation = e.getPresentation(); + presentation.setText(DebuggerBundle.message("action.interrupt.thread.text")); + presentation.setVisible(visible); + presentation.setEnabled(enabled); + } +} diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/ContextUtil.java b/java/debugger/impl/src/com/intellij/debugger/engine/ContextUtil.java index 161fb5846467..e29971ff4bfc 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/ContextUtil.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/ContextUtil.java @@ -39,6 +39,9 @@ public class ContextUtil { @Nullable public static SourcePosition getSourcePosition(final StackFrameContext context) { + if (context == null) { + return null; + } DebugProcessImpl debugProcess = (DebugProcessImpl)context.getDebugProcess(); if(debugProcess == null) { return null; diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessAdapterImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessAdapterImpl.java index 709f6da28cb7..cc5a975438d5 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessAdapterImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessAdapterImpl.java @@ -18,6 +18,7 @@ package com.intellij.debugger.engine; import com.intellij.execution.ExecutionException; import com.intellij.execution.configurations.RemoteConnection; import com.intellij.execution.configurations.RunProfileState; +import com.sun.jdi.ThreadReference; /** * @author lex @@ -66,6 +67,14 @@ public class DebugProcessAdapterImpl implements DebugProcessListener { //To change body of implemented methods use File | Settings | File Templates. } + //executed in manager thread + public void threadStarted(DebugProcess proc, ThreadReference thread) { + } + + //executed in manager thread + public void threadStopped(DebugProcess proc, ThreadReference thread) { + } + public void attachException(RunProfileState state, ExecutionException exception, RemoteConnection remoteConnection) { } } 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 cb13f6b67b60..55c31d7c4dbe 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessEvents.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessEvents.java @@ -44,6 +44,9 @@ import com.sun.jdi.VMDisconnectedException; import com.sun.jdi.VirtualMachine; import com.sun.jdi.event.*; import com.sun.jdi.request.EventRequest; +import com.sun.jdi.request.EventRequestManager; +import com.sun.jdi.request.ThreadDeathRequest; +import com.sun.jdi.request.ThreadStartRequest; /** * @author lex @@ -132,20 +135,43 @@ public class DebugProcessEvents extends DebugProcessImpl { while (!isStopped()) { try { final EventSet eventSet = eventQueue.remove(); - if (myReturnValueWatcher != null && myReturnValueWatcher.isEnabled()) { - int processed = 0; - for (EventIterator eventIterator = eventSet.eventIterator(); eventIterator.hasNext();) { - final Event event = eventIterator.nextEvent(); + + final boolean methodWatcherActive = myReturnValueWatcher != null && myReturnValueWatcher.isEnabled(); + int processed = 0; + for (EventIterator eventIterator = eventSet.eventIterator(); eventIterator.hasNext();) { + final Event event = eventIterator.nextEvent(); + + if (methodWatcherActive) { if (event instanceof MethodExitEvent) { if (myReturnValueWatcher.processMethodExitEvent((MethodExitEvent)event)) { processed++; } + continue; } } - if (processed == eventSet.size()) { - eventSet.resume(); - continue; + if (event instanceof ThreadStartEvent) { + processed++; + final ThreadReference thread = ((ThreadStartEvent)event).thread(); + getManagerThread().schedule(new DebuggerCommandImpl() { + protected void action() throws Exception { + myDebugProcessDispatcher.getMulticaster().threadStarted(DebugProcessEvents.this, thread); + } + }); } + else if (event instanceof ThreadDeathEvent) { + processed++; + final ThreadReference thread = ((ThreadDeathEvent)event).thread(); + getManagerThread().schedule(new DebuggerCommandImpl() { + protected void action() throws Exception { + myDebugProcessDispatcher.getMulticaster().threadStopped(DebugProcessEvents.this, thread); + } + }); + } + } + + if (processed == eventSet.size()) { + eventSet.resume(); + continue; } getManagerThread().invokeAndWait(new DebuggerCommandImpl() { @@ -262,10 +288,19 @@ public class DebugProcessEvents extends DebugProcessImpl { LOG.assertTrue(!isAttached()); if(myState.compareAndSet(STATE_INITIAL, STATE_ATTACHED)) { final VirtualMachineProxyImpl machineProxy = getVirtualMachineProxy(); + final EventRequestManager requestManager = machineProxy.eventRequestManager(); + if (machineProxy.canGetMethodReturnValues()) { - myReturnValueWatcher = new MethodReturnValueWatcher(machineProxy.eventRequestManager()); + myReturnValueWatcher = new MethodReturnValueWatcher(requestManager); } + final ThreadStartRequest threadStartRequest = requestManager.createThreadStartRequest(); + threadStartRequest.setSuspendPolicy(EventRequest.SUSPEND_NONE); + threadStartRequest.enable(); + final ThreadDeathRequest threadDeathRequest = requestManager.createThreadDeathRequest(); + threadDeathRequest.setSuspendPolicy(EventRequest.SUSPEND_NONE); + threadDeathRequest.enable(); + DebuggerManagerEx.getInstanceEx(getProject()).getBreakpointManager().setInitialBreakpointsState(); myDebugProcessDispatcher.getMulticaster().processAttached(this); diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/SuspendManagerUtil.java b/java/debugger/impl/src/com/intellij/debugger/engine/SuspendManagerUtil.java index 1b6b4e18be92..a27469416e80 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/SuspendManagerUtil.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/SuspendManagerUtil.java @@ -113,6 +113,9 @@ public class SuspendManagerUtil { } public static SuspendContextImpl getSuspendContextForThread(SuspendContextImpl suspendContext, ThreadReferenceProxyImpl thread) { + if (suspendContext == null) { + return null; + } SuspendContextImpl context = findContextByThread(suspendContext.getDebugProcess().getSuspendManager(), thread); return context != null && !context.myInProgress ? context : suspendContext; } 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 a2e37db95d34..876b86c71070 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerSession.java +++ b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerSession.java @@ -26,6 +26,7 @@ import com.intellij.debugger.engine.events.SuspendContextCommandImpl; import com.intellij.debugger.engine.requests.RequestManagerImpl; import com.intellij.debugger.jdi.StackFrameProxyImpl; import com.intellij.debugger.jdi.ThreadReferenceProxyImpl; +import com.intellij.debugger.jdi.VirtualMachineProxyImpl; import com.intellij.debugger.ui.breakpoints.Breakpoint; import com.intellij.debugger.ui.breakpoints.BreakpointWithHighlighter; import com.intellij.debugger.ui.breakpoints.LineBreakpoint; @@ -565,6 +566,26 @@ public class DebuggerSession implements AbstractDebuggerSession { }); mySteppingThroughThreads.clear(); } + + public void threadStarted(DebugProcess proc, ThreadReference thread) { + ((VirtualMachineProxyImpl)proc.getVirtualMachineProxy()).threadStarted(thread); + DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() { + public void run() { + final DebuggerStateManager contextManager = getContextManager(); + contextManager.fireStateChanged(contextManager.getContext(), EVENT_REFRESH_VIEWS_ONLY); + } + }); + } + + public void threadStopped(DebugProcess proc, ThreadReference thread) { + ((VirtualMachineProxyImpl)proc.getVirtualMachineProxy()).threadStopped(thread); + DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() { + public void run() { + final DebuggerStateManager contextManager = getContextManager(); + contextManager.fireStateChanged(contextManager.getContext(), EVENT_REFRESH_VIEWS_ONLY); + } + }); + } } private class MyEvaluationListener implements EvaluationListener { diff --git a/java/debugger/impl/src/com/intellij/debugger/jdi/VirtualMachineProxyImpl.java b/java/debugger/impl/src/com/intellij/debugger/jdi/VirtualMachineProxyImpl.java index 384c00968ec2..42ba1f06407b 100644 --- a/java/debugger/impl/src/com/intellij/debugger/jdi/VirtualMachineProxyImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/jdi/VirtualMachineProxyImpl.java @@ -143,6 +143,22 @@ public class VirtualMachineProxyImpl implements JdiTimer, VirtualMachineProxy { return myAllThreads.values(); } + public void threadStarted(ThreadReference thread) { + DebuggerManagerThreadImpl.assertIsManagerThread(); + final Map allThreads = myAllThreads; + if (allThreads != null && !allThreads.containsKey(thread)) { + allThreads.put(thread, new ThreadReferenceProxyImpl(this, thread)); + } + } + + public void threadStopped(ThreadReference thread) { + DebuggerManagerThreadImpl.assertIsManagerThread(); + final Map allThreads = myAllThreads; + if (allThreads != null) { + allThreads.remove(thread); + } + } + public void suspend() { DebuggerManagerThreadImpl.assertIsManagerThread(); myPausePressedCount++; @@ -564,7 +580,7 @@ public class VirtualMachineProxyImpl implements JdiTimer, VirtualMachineProxy { if (!myNestedClassesCache.isEmpty()) { myNestedClassesCache = new HashMap>(myNestedClassesCache.size()); } - myAllThreadsDirty = true; + //myAllThreadsDirty = true; myTimeStamp++; } diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerSessionTab.java b/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerSessionTab.java index 9476c8f92562..152176247a77 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerSessionTab.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerSessionTab.java @@ -46,7 +46,6 @@ import com.intellij.execution.ui.layout.LayoutViewOptions; import com.intellij.execution.ui.layout.PlaceInGrid; import com.intellij.ide.CommonActionsManager; import com.intellij.ide.actions.ContextHelpAction; -import com.intellij.ide.impl.ProjectUtil; import com.intellij.idea.ActionsBundle; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; @@ -55,7 +54,6 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.IconLoader; -import com.intellij.openapi.util.registry.Registry; import com.intellij.ui.content.AlertIcon; import com.intellij.ui.content.Content; import com.intellij.ui.content.ContentManagerAdapter; @@ -466,9 +464,7 @@ public class DebuggerSessionTab extends DebuggerSessionTabBase implements Dispos } } - public RunContentDescriptor attachToSession(final DebuggerSession session, - final ProgramRunner runner, - final ExecutionEnvironment env) + public RunContentDescriptor attachToSession(final DebuggerSession session, final ProgramRunner runner, final ExecutionEnvironment env) throws ExecutionException { disposeSession(); myDebuggerSession = session; @@ -487,7 +483,7 @@ public class DebuggerSessionTab extends DebuggerSessionTabBase implements Dispos myStateManager.fireStateChanged(newContext, event); } }); - return initUI(getDebugProcess().getExecutionResult()); + return initUI(session.getProcess().getExecutionResult()); } private void attractFramesOnPause(final int event) { diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/FrameDebuggerTree.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/FrameDebuggerTree.java index e6d3160498c3..5caa37b9bf71 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/FrameDebuggerTree.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/FrameDebuggerTree.java @@ -29,6 +29,7 @@ import com.intellij.debugger.engine.evaluation.EvaluateException; import com.intellij.debugger.engine.evaluation.EvaluationContextImpl; import com.intellij.debugger.engine.evaluation.TextWithImports; import com.intellij.debugger.engine.evaluation.TextWithImportsImpl; +import com.intellij.debugger.engine.events.DebuggerCommandImpl; import com.intellij.debugger.impl.DebuggerContextImpl; import com.intellij.debugger.impl.DebuggerSession; import com.intellij.debugger.jdi.LocalVariableProxyImpl; @@ -104,7 +105,7 @@ public class FrameDebuggerTree extends DebuggerTree { } - protected BuildNodeCommand getBuildNodeCommand(final DebuggerTreeNodeImpl node) { + protected DebuggerCommandImpl getBuildNodeCommand(final DebuggerTreeNodeImpl node) { if (node.getDescriptor() instanceof StackFrameDescriptorImpl) { return new BuildFrameTreeVariablesCommand(node); } diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/ThreadsDebuggerTree.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/ThreadsDebuggerTree.java index 737bbeeb135c..c61ece98c151 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/ThreadsDebuggerTree.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/ThreadsDebuggerTree.java @@ -17,14 +17,18 @@ package com.intellij.debugger.ui.impl; import com.intellij.debugger.DebuggerInvocationUtil; import com.intellij.debugger.engine.DebugProcessImpl; +import com.intellij.debugger.engine.SuspendContextImpl; import com.intellij.debugger.engine.evaluation.EvaluationContextImpl; +import com.intellij.debugger.engine.events.DebuggerCommandImpl; import com.intellij.debugger.impl.DebuggerContextImpl; +import com.intellij.debugger.impl.DebuggerSession; import com.intellij.debugger.jdi.StackFrameProxyImpl; import com.intellij.debugger.jdi.ThreadGroupReferenceProxyImpl; import com.intellij.debugger.jdi.ThreadReferenceProxyImpl; import com.intellij.debugger.jdi.VirtualMachineProxyImpl; import com.intellij.debugger.settings.ThreadsViewSettings; import com.intellij.debugger.ui.impl.watch.*; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.util.ui.tree.TreeModelAdapter; @@ -66,28 +70,43 @@ public class ThreadsDebuggerTree extends DebuggerTree { } protected void build(DebuggerContextImpl context) { - buildWhenPaused(context, new RefreshThreadsTreelCommand(context)); + DebuggerSession debuggerSession = context.getDebuggerSession(); + final RefreshThreadsTreeCommand command = new RefreshThreadsTreeCommand(debuggerSession); + + final int state = debuggerSession.getState(); + if (ApplicationManager.getApplication().isUnitTestMode() || state == DebuggerSession.STATE_PAUSED || state == DebuggerSession.STATE_RUNNING) { + showMessage(MessageDescriptor.EVALUATING); + context.getDebugProcess().getManagerThread().schedule(command); + } + else { + showMessage(debuggerSession.getStateDescription()); + } } - private class RefreshThreadsTreelCommand extends RefreshDebuggerTreeCommand{ - public RefreshThreadsTreelCommand(DebuggerContextImpl context) { - super(context); + private class RefreshThreadsTreeCommand extends DebuggerCommandImpl{ + private final DebuggerSession mySession; + + public RefreshThreadsTreeCommand(DebuggerSession session) { + mySession = session; } - public void contextAction() throws Exception { + protected void action() throws Exception { final DebuggerTreeNodeImpl root = getNodeFactory().getDefaultNode(); + final DebugProcessImpl debugProcess = mySession.getProcess(); + if(debugProcess == null || !debugProcess.isAttached()) { + return; + } + final DebuggerContextImpl context = mySession.getContextManager().getContext(); + final SuspendContextImpl suspendContext = context.getSuspendContext(); + final ThreadReferenceProxyImpl suspendContextThread = suspendContext != null? suspendContext.getThread() : null; + final boolean showGroups = ThreadsViewSettings.getInstance().SHOW_THREAD_GROUPS; try { - DebugProcessImpl debugProcess = getDebuggerContext().getDebugProcess(); - if(debugProcess == null || !debugProcess.isAttached()) { - return; - } - - final ThreadReferenceProxyImpl currentThread = ThreadsViewSettings.getInstance().SHOW_CURRENT_THREAD ? getSuspendContext().getThread() : null; + final ThreadReferenceProxyImpl currentThread = ThreadsViewSettings.getInstance().SHOW_CURRENT_THREAD ? suspendContextThread : null; final VirtualMachineProxyImpl vm = debugProcess.getVirtualMachineProxy(); - final EvaluationContextImpl evaluationContext = getDebuggerContext().createEvaluationContext(); + final EvaluationContextImpl evaluationContext = suspendContext != null? getDebuggerContext().createEvaluationContext() : null; final NodeManagerImpl nodeManager = getNodeFactory(); if (showGroups) { @@ -139,12 +158,11 @@ public class ThreadsDebuggerTree extends DebuggerTree { } } - final ThreadReferenceProxyImpl thread = getSuspendContext().getThread(); - final boolean hasThreadToSelect = thread != null; // thread can be null if pause was pressed + final boolean hasThreadToSelect = suspendContextThread != null; // thread can be null if pause was pressed final List groups; if (hasThreadToSelect && showGroups) { groups = new ArrayList(); - for(ThreadGroupReferenceProxyImpl group = thread.threadGroupProxy(); group != null; group = group.parent()) { + for(ThreadGroupReferenceProxyImpl group = suspendContextThread.threadGroupProxy(); group != null; group = group.parent()) { groups.add(group); } Collections.reverse(groups); @@ -158,7 +176,7 @@ public class ThreadsDebuggerTree extends DebuggerTree { getMutableModel().setRoot(root); treeChanged(); if (hasThreadToSelect) { - selectThread(groups, thread, true); + selectThread(groups, suspendContextThread, true); } } }); @@ -166,8 +184,6 @@ public class ThreadsDebuggerTree extends DebuggerTree { private void selectThread(final List pathToThread, final ThreadReferenceProxyImpl thread, final boolean expand) { LOG.assertTrue(SwingUtilities.isEventDispatchThread()); - - class MyTreeModelAdapter extends TreeModelAdapter { private void structureChanged(DebuggerTreeNodeImpl node) { for(Enumeration enumeration = node.children(); enumeration.hasMoreElements(); ) { diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/ThreadsPanel.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/ThreadsPanel.java index 2f8e6e118c4e..f6d2116e7442 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/ThreadsPanel.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/ThreadsPanel.java @@ -17,20 +17,25 @@ package com.intellij.debugger.ui.impl; import com.intellij.debugger.actions.DebuggerAction; import com.intellij.debugger.actions.DebuggerActions; -import com.intellij.debugger.impl.DebuggerContextUtil; -import com.intellij.debugger.impl.DebuggerStateManager; +import com.intellij.debugger.engine.DebugProcessImpl; +import com.intellij.debugger.engine.events.DebuggerCommandImpl; +import com.intellij.debugger.impl.*; import com.intellij.debugger.jdi.StackFrameProxyImpl; import com.intellij.debugger.ui.impl.watch.DebuggerTree; import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl; import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl; import com.intellij.debugger.ui.impl.watch.StackFrameDescriptorImpl; +import com.intellij.debugger.ui.tree.render.DescriptorLabelListener; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.ActionPopupMenu; import com.intellij.openapi.actionSystem.DefaultActionGroup; import com.intellij.openapi.actionSystem.PlatformDataKeys; +import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Disposer; import com.intellij.ui.ScrollPaneFactory; +import com.intellij.util.Alarm; import org.jetbrains.annotations.NonNls; import java.awt.*; @@ -39,8 +44,10 @@ import java.awt.event.KeyEvent; public class ThreadsPanel extends DebuggerTreePanel{ @NonNls private static final String HELP_ID = "debugging.debugThreads"; + private final Alarm myUpdateLabelsAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD); + private static final int LABELS_UPDATE_DELAY_MS = 200; - public ThreadsPanel(Project project, DebuggerStateManager stateManager) { + public ThreadsPanel(Project project, final DebuggerStateManager stateManager) { super(project, stateManager); final Disposable disposable = DebuggerAction.installEditAction(getThreadsTree(), DebuggerActions.EDIT_FRAME_SOURCE); @@ -60,8 +67,88 @@ public class ThreadsPanel extends DebuggerTreePanel{ } }); add(ScrollPaneFactory.createScrollPane(getThreadsTree()), BorderLayout.CENTER); + stateManager.addListener(new DebuggerContextListener() { + public void changeEvent(DebuggerContextImpl newContext, int event) { + if (DebuggerSession.EVENT_ATTACHED == event || DebuggerSession.EVENT_RESUME == event) { + startLabelsUpdate(); + } + else if (DebuggerSession.EVENT_PAUSE == event || DebuggerSession.EVENT_DETACHED == event || DebuggerSession.EVENT_DISPOSE == event) { + myUpdateLabelsAlarm.cancelAllRequests(); + } + if (DebuggerSession.EVENT_DETACHED == event || DebuggerSession.EVENT_DISPOSE == event) { + stateManager.removeListener(this); + } + } + }); + startLabelsUpdate(); } + private void startLabelsUpdate() { + myUpdateLabelsAlarm.cancelAllRequests(); + myUpdateLabelsAlarm.addRequest(new Runnable() { + public void run() { + boolean updateScheduled = false; + try { + if (isUpdateEnabled()) { + final ThreadsDebuggerTree tree = getThreadsTree(); + final DebuggerTreeNodeImpl root = (DebuggerTreeNodeImpl)tree.getModel().getRoot(); + if (root != null) { + final DebugProcessImpl process = getContext().getDebugProcess(); + if (process != null) { + process.getManagerThread().invoke(new DebuggerCommandImpl() { + protected void action() throws Exception { + try { + updateNodeLabels(root); + } + finally { + reschedule(); + } + } + protected void commandCancelled() { + reschedule(); + } + }); + updateScheduled = true; + } + } + } + } + finally { + if (!updateScheduled) { + reschedule(); + } + } + } + + private void reschedule() { + final DebuggerSession session = getContext().getDebuggerSession(); + if (session.isAttached() && !session.isPaused()) { + myUpdateLabelsAlarm.addRequest(this, LABELS_UPDATE_DELAY_MS, ModalityState.NON_MODAL); + } + } + + }, LABELS_UPDATE_DELAY_MS, ModalityState.NON_MODAL); + } + + @Override + public void dispose() { + Disposer.dispose(myUpdateLabelsAlarm); + super.dispose(); + } + + private static void updateNodeLabels(DebuggerTreeNodeImpl from) { + final int childCount = from.getChildCount(); + for (int idx = 0; idx < childCount; idx++) { + final DebuggerTreeNodeImpl child = (DebuggerTreeNodeImpl)from.getChildAt(idx); + child.getDescriptor().updateRepresentation(null, new DescriptorLabelListener() { + public void labelChanged() { + child.labelChanged(); + } + }); + updateNodeLabels(child); + } + } + protected DebuggerTree createTreeView() { return new ThreadsDebuggerTree(getProject()); } diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/UpdatableDebuggerView.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/UpdatableDebuggerView.java index a7319092470c..f2abc68402f2 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/UpdatableDebuggerView.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/UpdatableDebuggerView.java @@ -23,9 +23,9 @@ import com.intellij.debugger.ui.DebuggerView; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.AnAction; -import com.intellij.openapi.actionSystem.CustomShortcutSet; import com.intellij.openapi.actionSystem.ShortcutSet; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Disposer; import com.intellij.util.Alarm; import com.sun.jdi.VMDisconnectedException; @@ -37,7 +37,7 @@ public abstract class UpdatableDebuggerView extends JPanel implements DebuggerVi private final Project myProject; private final DebuggerStateManager myStateManager; protected final Alarm myRebuildAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD); - private volatile boolean myRefreshNeeded = true; + protected volatile boolean myRefreshNeeded = true; protected final java.util.List myDisposables = new ArrayList(); private boolean myUpdateEnabled; @@ -118,9 +118,9 @@ public abstract class UpdatableDebuggerView extends JPanel implements DebuggerVi } public void dispose() { - myRebuildAlarm.dispose(); + Disposer.dispose(myRebuildAlarm); for (Disposable disposable : myDisposables) { - disposable.dispose(); + Disposer.dispose(disposable); } myDisposables.clear(); } diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTree.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTree.java index 82dcc22eee34..a94bc96b927e 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTree.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTree.java @@ -23,8 +23,10 @@ package com.intellij.debugger.ui.impl.watch; import com.intellij.debugger.DebuggerBundle; import com.intellij.debugger.DebuggerInvocationUtil; import com.intellij.debugger.engine.DebugProcessImpl; +import com.intellij.debugger.engine.SuspendContextImpl; import com.intellij.debugger.engine.evaluation.EvaluateException; import com.intellij.debugger.engine.evaluation.EvaluationContextImpl; +import com.intellij.debugger.engine.events.DebuggerCommandImpl; import com.intellij.debugger.engine.events.DebuggerContextCommandImpl; import com.intellij.debugger.engine.events.SuspendContextCommandImpl; import com.intellij.debugger.impl.DebuggerContextImpl; @@ -198,16 +200,16 @@ public abstract class DebuggerTree extends DebuggerTreeBase implements DataProvi } final DebugProcessImpl debugProcess = getDebuggerContext().getDebugProcess(); if (debugProcess != null) { - BuildNodeCommand command = getBuildNodeCommand(node); + DebuggerCommandImpl command = getBuildNodeCommand(node); if (command != null) { - command.getNode().add(myNodeManager.createMessageNode(MessageDescriptor.EVALUATING)); + node.add(myNodeManager.createMessageNode(MessageDescriptor.EVALUATING)); debugProcess.getManagerThread().schedule(command); } } } // todo: convert "if" into instance method call - protected BuildNodeCommand getBuildNodeCommand(final DebuggerTreeNodeImpl node) { + protected DebuggerCommandImpl getBuildNodeCommand(final DebuggerTreeNodeImpl node) { if (node.getDescriptor() instanceof StackFrameDescriptorImpl) { return new BuildStackFrameCommand(node); } @@ -668,19 +670,24 @@ public abstract class DebuggerTree extends DebuggerTreeBase implements DataProvi } } - private class BuildThreadGroupCommand extends BuildNodeCommand { + private class BuildThreadGroupCommand extends DebuggerCommandImpl { + private final DebuggerTreeNodeImpl myNode; + protected final List myChildren = new LinkedList(); + public BuildThreadGroupCommand(DebuggerTreeNodeImpl node) { - super(node); + myNode = node; } - public void threadAction() { - ThreadGroupDescriptorImpl groupDescriptor = (ThreadGroupDescriptorImpl)getNode().getDescriptor(); + protected void action() throws Exception { + ThreadGroupDescriptorImpl groupDescriptor = (ThreadGroupDescriptorImpl)myNode.getDescriptor(); ThreadGroupReferenceProxyImpl threadGroup = groupDescriptor.getThreadGroupReference(); List threads = new ArrayList(threadGroup.threads()); Collections.sort(threads, ThreadReferenceProxyImpl.ourComparator); - EvaluationContextImpl evaluationContext = getDebuggerContext().createEvaluationContext(); + final DebuggerContextImpl debuggerContext = getDebuggerContext(); + final SuspendContextImpl suspendContext = debuggerContext.getSuspendContext(); + final EvaluationContextImpl evaluationContext = suspendContext != null? debuggerContext.createEvaluationContext() : null; boolean showCurrent = ThreadsViewSettings.getInstance().SHOW_CURRENT_THREAD; @@ -702,8 +709,7 @@ public abstract class DebuggerTree extends DebuggerTreeBase implements DataProvi for (ThreadReferenceProxyImpl thread : threads) { if (thread != null) { - DebuggerTreeNodeImpl threadNode = - myNodeManager.createNode(myNodeManager.getThreadDescriptor(groupDescriptor, thread), evaluationContext); + final DebuggerTreeNodeImpl threadNode = myNodeManager.createNode(myNodeManager.getThreadDescriptor(groupDescriptor, thread), evaluationContext); if (showCurrent && ((ThreadDescriptorImpl)threadNode.getDescriptor()).isCurrent()) { threadNodes.add(0, threadNode); } @@ -717,6 +723,18 @@ public abstract class DebuggerTree extends DebuggerTreeBase implements DataProvi updateUI(true); } + + protected void updateUI(final boolean scrollToVisible) { + DebuggerInvocationUtil.swingInvokeLater(getProject(), new Runnable() { + public void run() { + myNode.removeAllChildren(); + for (DebuggerTreeNode debuggerTreeNode : myChildren) { + myNode.add(debuggerTreeNode); + } + myNode.childrenChanged(scrollToVisible); + } + }); + } } public void hideTooltip() { diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ThisDescriptorImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ThisDescriptorImpl.java index cfd268b2f025..67e8304ff5bf 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ThisDescriptorImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ThisDescriptorImpl.java @@ -37,7 +37,7 @@ public class ThisDescriptorImpl extends ValueDescriptorImpl{ } public Value calcValue(EvaluationContextImpl evaluationContext) throws EvaluateException { - return evaluationContext.getThisObject(); + return evaluationContext != null? evaluationContext.getThisObject() : null; } public String getName() { diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ThreadDescriptorImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ThreadDescriptorImpl.java index 8a8cb7a9527b..a35cc7e08e62 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ThreadDescriptorImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ThreadDescriptorImpl.java @@ -70,7 +70,6 @@ public class ThreadDescriptorImpl extends NodeDescriptorImpl implements ThreadDe if (grname != null && !"SYSTEM".equalsIgnoreCase(grname)) { return DebuggerBundle.message("label.thread.node.in.group", myName, thread.uniqueID(), threadStatusText, grname); } - return DebuggerBundle.message("label.thread.node", myName, thread.uniqueID(), threadStatusText); } catch (ObjectCollectedException e) { @@ -96,20 +95,20 @@ public class ThreadDescriptorImpl extends NodeDescriptorImpl implements ThreadDe public void setContext(EvaluationContextImpl context) { final ThreadReferenceProxyImpl thread = getThreadReference(); - final SuspendManager suspendManager = context.getDebugProcess().getSuspendManager(); - final SuspendContextImpl suspendContext = context.getSuspendContext(); + final SuspendManager suspendManager = context != null? context.getDebugProcess().getSuspendManager() : null; + final SuspendContextImpl suspendContext = context != null? context.getSuspendContext() : null; try { - myIsSuspended = suspendManager.isSuspended(thread); + myIsSuspended = suspendManager != null? suspendManager.isSuspended(thread) : thread.isSuspended(); } catch (ObjectCollectedException e) { myIsSuspended = false; } myIsExpandable = calcExpandable(myIsSuspended); mySuspendContext = SuspendManagerUtil.getSuspendContextForThread(suspendContext, thread); - myIsAtBreakpoint = SuspendManagerUtil.findContextByThread(suspendManager, thread) != null; - myIsCurrent = suspendContext.getThread() == thread; - myIsFrozen = suspendManager.isFrozen(thread); + myIsAtBreakpoint = suspendManager != null? SuspendManagerUtil.findContextByThread(suspendManager, thread) != null : thread.getThreadReference().isAtBreakpoint(); + myIsCurrent = suspendContext != null? suspendContext.getThread() == thread : false; + myIsFrozen = suspendManager != null? suspendManager.isFrozen(thread) : myIsSuspended; } private boolean calcExpandable(final boolean isSuspended) { diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ThreadGroupDescriptorImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ThreadGroupDescriptorImpl.java index a9c8333b5210..2f0b3044edf6 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ThreadGroupDescriptorImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ThreadGroupDescriptorImpl.java @@ -15,6 +15,7 @@ */ package com.intellij.debugger.ui.impl.watch; +import com.intellij.debugger.DebuggerBundle; import com.intellij.debugger.engine.DebuggerManagerThreadImpl; import com.intellij.debugger.engine.evaluation.EvaluateException; import com.intellij.debugger.engine.evaluation.EvaluationContextImpl; @@ -22,7 +23,6 @@ import com.intellij.debugger.jdi.ThreadGroupReferenceProxyImpl; import com.intellij.debugger.jdi.ThreadReferenceProxyImpl; import com.intellij.debugger.ui.tree.ThreadGroupDescriptor; import com.intellij.debugger.ui.tree.render.DescriptorLabelListener; -import com.intellij.debugger.DebuggerBundle; import com.sun.jdi.ObjectCollectedException; public class ThreadGroupDescriptorImpl extends NodeDescriptorImpl implements ThreadGroupDescriptor{ @@ -64,7 +64,7 @@ public class ThreadGroupDescriptorImpl extends NodeDescriptorImpl implements Thr } public void setContext(EvaluationContextImpl context) { - ThreadReferenceProxyImpl threadProxy = context.getSuspendContext().getThread(); + ThreadReferenceProxyImpl threadProxy = context != null? context.getSuspendContext().getThread() : null; myIsCurrent = threadProxy != null && isDescendantGroup(threadProxy.threadGroupProxy()); myIsExpandable = calcExpandable(); } diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ValueDescriptorImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ValueDescriptorImpl.java index 18044589011a..8039f7083821 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ValueDescriptorImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ValueDescriptorImpl.java @@ -170,7 +170,7 @@ public abstract class ValueDescriptorImpl extends NodeDescriptorImpl implements @Nullable private static ObjectReference getTargetExceptionWithStackTraceFilled(final EvaluationContextImpl evaluationContext, EvaluateException ex){ final ObjectReference exceptionObj = ex.getExceptionFromTargetVM(); - if (exceptionObj != null) { + if (exceptionObj != null && evaluationContext != null) { try { final ReferenceType refType = exceptionObj.referenceType(); final List methods = refType.methodsByName("getStackTrace", "()[Ljava/lang/StackTraceElement;"); diff --git a/java/debugger/openapi/src/com/intellij/debugger/engine/DebugProcessAdapter.java b/java/debugger/openapi/src/com/intellij/debugger/engine/DebugProcessAdapter.java index c5dcfb5fce31..16c43c2b27c7 100644 --- a/java/debugger/openapi/src/com/intellij/debugger/engine/DebugProcessAdapter.java +++ b/java/debugger/openapi/src/com/intellij/debugger/engine/DebugProcessAdapter.java @@ -18,6 +18,7 @@ package com.intellij.debugger.engine; import com.intellij.execution.ExecutionException; import com.intellij.execution.configurations.RemoteConnection; import com.intellij.execution.configurations.RunProfileState; +import com.sun.jdi.ThreadReference; public class DebugProcessAdapter implements DebugProcessListener{ //executed in manager thread @@ -44,6 +45,14 @@ public class DebugProcessAdapter implements DebugProcessListener{ } + //executed in manager thread + public void threadStarted(DebugProcess proc, ThreadReference thread) { + } + + //executed in manager thread + public void threadStopped(DebugProcess proc, ThreadReference thread) { + } + public void attachException(RunProfileState state, ExecutionException exception, RemoteConnection remoteConnection) { } diff --git a/java/debugger/openapi/src/com/intellij/debugger/engine/DebugProcessListener.java b/java/debugger/openapi/src/com/intellij/debugger/engine/DebugProcessListener.java index 059d7eb488b3..e992f25b2595 100644 --- a/java/debugger/openapi/src/com/intellij/debugger/engine/DebugProcessListener.java +++ b/java/debugger/openapi/src/com/intellij/debugger/engine/DebugProcessListener.java @@ -18,6 +18,7 @@ package com.intellij.debugger.engine; import com.intellij.execution.ExecutionException; import com.intellij.execution.configurations.RemoteConnection; import com.intellij.execution.configurations.RunProfileState; +import com.sun.jdi.ThreadReference; import java.util.EventListener; @@ -39,5 +40,9 @@ public interface DebugProcessListener extends EventListener{ void processAttached(DebugProcess process); void attachException(RunProfileState state, ExecutionException exception, RemoteConnection remoteConnection); + + void threadStarted(DebugProcess proc, ThreadReference thread); + + void threadStopped(DebugProcess proc, ThreadReference thread); } diff --git a/platform/platform-resources-en/src/messages/ActionsBundle.properties b/platform/platform-resources-en/src/messages/ActionsBundle.properties index 9e1ac21ca82e..0b0b3a54f6f1 100644 --- a/platform/platform-resources-en/src/messages/ActionsBundle.properties +++ b/platform/platform-resources-en/src/messages/ActionsBundle.properties @@ -829,6 +829,7 @@ action.Debugger.SetValue.text=Set Value... action.Debugger.ShowFrame.text=Show Frame action.Debugger.ResumeThread.text=Resume action.Debugger.FreezeThread.text=Freeze +action.Debugger.InterruptThread.text=Interrupt action.Debugger.MuteBreakpoints.text=Mute Breakpoints action.Debugger.MuteBreakpoints.description=Mute/unmute all breakpoints in a debug session. action.Debugger.MarkObject.text=Mark Object... diff --git a/resources-en/src/messages/DebuggerBundle.properties b/resources-en/src/messages/DebuggerBundle.properties index 675837fda83b..e93687b2dc73 100644 --- a/resources-en/src/messages/DebuggerBundle.properties +++ b/resources-en/src/messages/DebuggerBundle.properties @@ -31,6 +31,7 @@ action.remove.watch.text={0,choice, 1#Remove Watch|2#Remove Watches} progress.evaluating=Evaluating {0} action.resume.thread.text.resume=Resume action.resume.thread.text.unfreeze=Unfreeze +action.interrupt.thread.text=Interrupt title.set.value=Set Value warning.recalculate=The value will be recalculated progress.set.value=Setting value... diff --git a/resources/src/idea/IdeaActions.xml b/resources/src/idea/IdeaActions.xml index 2a0d68d4b3d6..d6d023194d46 100644 --- a/resources/src/idea/IdeaActions.xml +++ b/resources/src/idea/IdeaActions.xml @@ -308,6 +308,7 @@ + @@ -591,6 +592,7 @@ +