diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/action/ClassesActionBase.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/ClassesActionBase.java index f9d148da405b..45be8c711db9 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/action/ClassesActionBase.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/action/ClassesActionBase.java @@ -15,13 +15,12 @@ */ package com.intellij.debugger.memory.action; +import com.intellij.debugger.memory.ui.ClassesTable; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.project.Project; -import com.intellij.xdebugger.XDebugSession; import com.sun.jdi.ReferenceType; import org.jetbrains.annotations.Nullable; -import com.intellij.debugger.memory.ui.ClassesTable; public abstract class ClassesActionBase extends AnAction { @Override @@ -35,7 +34,7 @@ public abstract class ClassesActionBase extends AnAction { } protected boolean isEnabled(AnActionEvent e) { - Project project = e.getProject(); + final Project project = e.getProject(); return project != null && !project.isDisposed(); } @@ -45,9 +44,4 @@ public abstract class ClassesActionBase extends AnAction { protected ReferenceType getSelectedClass(AnActionEvent e) { return e.getData(ClassesTable.SELECTED_CLASS_KEY); } - - @Nullable - XDebugSession getDebugSession(AnActionEvent e) { - return e.getData(ClassesTable.DEBUG_SESSION_KEY); - } } diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/action/JumpToTypeSourceAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/JumpToTypeSourceAction.java index ea4c30ecdcfb..f3a03b85373a 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/action/JumpToTypeSourceAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/action/JumpToTypeSourceAction.java @@ -21,8 +21,9 @@ import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiClass; import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.xdebugger.XDebugSession; -import com.sun.jdi.*; +import com.sun.jdi.ArrayType; +import com.sun.jdi.ReferenceType; +import com.sun.jdi.VirtualMachine; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -47,17 +48,15 @@ public class JumpToTypeSourceAction extends ClassesActionBase { @Nullable private PsiClass getPsiClass(AnActionEvent e) { - ReferenceType selectedClass = getSelectedClass(e); - XDebugSession session = getDebugSession(e); - if (selectedClass == null || session == null) { + final ReferenceType selectedClass = getSelectedClass(e); + final Project project = e.getProject(); + if (selectedClass == null || project == null) { return null; } - ReferenceType targetClass = getObjectType(selectedClass); + final ReferenceType targetClass = getObjectType(selectedClass); if (targetClass != null) { - Project project = session.getProject(); - return DebuggerUtils - .findClass(targetClass.name(), project, GlobalSearchScope.allScope(project)); + return DebuggerUtils.findClass(targetClass.name(), project, GlobalSearchScope.allScope(project)); } return null; @@ -69,8 +68,8 @@ public class JumpToTypeSourceAction extends ClassesActionBase { return ref; } - String elementTypeName = ref.name().replace("[]", ""); - VirtualMachine vm = ref.virtualMachine(); + final String elementTypeName = ref.name().replace("[]", ""); + final VirtualMachine vm = ref.virtualMachine(); final List referenceTypes = vm.classesByName(elementTypeName); if (referenceTypes.size() == 1) { return referenceTypes.get(0); diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowInstancesFromClassesViewAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowInstancesFromClassesViewAction.java index 35ccc88f2c11..c52b5d027bfa 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowInstancesFromClassesViewAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowInstancesFromClassesViewAction.java @@ -16,13 +16,16 @@ package com.intellij.debugger.memory.action; import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.project.Project; import com.intellij.xdebugger.XDebugSession; +import com.intellij.xdebugger.XDebuggerManager; import com.sun.jdi.ReferenceType; import com.intellij.debugger.memory.ui.ClassesTable; import com.intellij.debugger.memory.ui.InstancesWindow; public class ShowInstancesFromClassesViewAction extends ShowInstancesAction { private static final String POPUP_ELEMENT_LABEL = "Show Instances"; + @Override protected boolean isEnabled(AnActionEvent e) { return super.isEnabled(e) && getSelectedClass(e) != null; @@ -30,10 +33,13 @@ public class ShowInstancesFromClassesViewAction extends ShowInstancesAction { @Override protected void perform(AnActionEvent e) { - XDebugSession debugSession = getDebugSession(e); - ReferenceType selectedClass = getSelectedClass(e); - if (debugSession != null && selectedClass != null) { - new InstancesWindow(debugSession, selectedClass::instances, selectedClass.name()).show(); + final Project project = e.getProject(); + final ReferenceType selectedClass = getSelectedClass(e); + if (project != null && selectedClass != null) { + final XDebugSession debugSession = XDebuggerManager.getInstance(project).getCurrentSession(); + if (debugSession != null) { + new InstancesWindow(debugSession, limit -> selectedClass.instances(limit), selectedClass.name()).show(); + } } } diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowNewInstancesAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowNewInstancesAction.java index bc3dc86e68c3..53fd0b7eef6f 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowNewInstancesAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowNewInstancesAction.java @@ -15,22 +15,23 @@ */ package com.intellij.debugger.memory.action; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.xdebugger.XDebugSession; -import com.sun.jdi.ReferenceType; -import com.intellij.debugger.memory.utils.InstancesProvider; import com.intellij.debugger.memory.ui.ClassesTable; import com.intellij.debugger.memory.ui.InstancesWindow; +import com.intellij.debugger.memory.utils.InstancesProvider; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.project.Project; +import com.intellij.xdebugger.XDebugSession; +import com.intellij.xdebugger.XDebuggerManager; +import com.sun.jdi.ReferenceType; public class ShowNewInstancesAction extends ShowInstancesAction { private static final String POPUP_ELEMENT_LABEL = "Show New Instances"; @Override protected boolean isEnabled(AnActionEvent e) { - XDebugSession session = getDebugSession(e); - ReferenceType selectedClass = getSelectedClass(e); - InstancesProvider provider = e.getData(ClassesTable.NEW_INSTANCES_PROVIDER_KEY); - return super.isEnabled(e) && session != null && selectedClass != null && provider != null; + final ReferenceType selectedClass = getSelectedClass(e); + final InstancesProvider provider = e.getData(ClassesTable.NEW_INSTANCES_PROVIDER_KEY); + return super.isEnabled(e) && selectedClass != null && provider != null; } @Override @@ -51,9 +52,13 @@ public class ShowNewInstancesAction extends ShowInstancesAction { @Override protected void perform(AnActionEvent e) { - XDebugSession session = getDebugSession(e); - ReferenceType selectedClass = getSelectedClass(e); - InstancesProvider provider = e.getData(ClassesTable.NEW_INSTANCES_PROVIDER_KEY); + final Project project = e.getProject(); + + final ReferenceType selectedClass = getSelectedClass(e); + final InstancesProvider provider = e.getData(ClassesTable.NEW_INSTANCES_PROVIDER_KEY); + final XDebugSession session = project != null + ? XDebuggerManager.getInstance(project).getCurrentSession() + : null; if (selectedClass != null && provider != null && session != null) { new InstancesWindow(session, provider, selectedClass.name()).show(); } diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/action/tracking/JumpToAllocationSourceAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/tracking/JumpToAllocationSourceAction.java index fdbbc170ca28..489ae76d6f06 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/action/tracking/JumpToAllocationSourceAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/action/tracking/JumpToAllocationSourceAction.java @@ -17,7 +17,9 @@ package com.intellij.debugger.memory.action.tracking; import com.intellij.debugger.DebuggerManager; import com.intellij.debugger.memory.action.DebuggerTreeAction; +import com.intellij.debugger.memory.component.MemoryViewDebugProcessData; import com.intellij.debugger.memory.ui.StackFramePopup; +import com.intellij.debugger.memory.utils.StackFrameItem; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.project.Project; import com.intellij.psi.search.GlobalSearchScope; @@ -27,9 +29,6 @@ import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; import com.sun.jdi.ObjectReference; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import com.intellij.debugger.memory.component.CreationPositionTracker; -import com.intellij.debugger.memory.utils.StackFrameItem; -import com.intellij.debugger.memory.ui.InstancesTree; import java.util.List; @@ -41,13 +40,13 @@ public class JumpToAllocationSourceAction extends DebuggerTreeAction { @Override protected void perform(XValueNodeImpl node, @NotNull String nodeName, AnActionEvent e) { - Project project = e.getProject(); - List stack = getStack(e); - if(project != null && stack != null) { - XDebugSession session = XDebuggerManager.getInstance(project).getCurrentSession(); - if(session != null) { - GlobalSearchScope searchScope = DebuggerManager.getInstance(project) - .getDebugProcess(session.getDebugProcess().getProcessHandler()).getSearchScope(); + final Project project = e.getProject(); + final List stack = getStack(e); + if (project != null && stack != null) { + final XDebugSession session = XDebuggerManager.getInstance(project).getCurrentSession(); + if (session != null) { + final GlobalSearchScope searchScope = DebuggerManager.getInstance(project) + .getDebugProcess(session.getDebugProcess().getProcessHandler()).getSearchScope(); new StackFramePopup(project, stack, searchScope).show(); } } @@ -55,15 +54,21 @@ public class JumpToAllocationSourceAction extends DebuggerTreeAction { @Nullable private List getStack(AnActionEvent e) { - Project project = e.getProject(); - XValueNodeImpl selectedNode = getSelectedNode(e.getDataContext()); - ObjectReference ref = selectedNode != null ? getObjectReference(selectedNode) : null; - if(project == null || ref == null) { + final Project project = e.getProject(); + final XValueNodeImpl selectedNode = getSelectedNode(e.getDataContext()); + final ObjectReference ref = selectedNode != null ? getObjectReference(selectedNode) : null; + if (project == null || ref == null) { return null; } - XDebugSession session = e.getData(InstancesTree.DEBUG_SESSION_DATA_KEY); - CreationPositionTracker tracker = CreationPositionTracker.getInstance(project); - return session == null || tracker == null ? null : tracker.getStack(session, ref); + final XDebugSession session = XDebuggerManager.getInstance(project).getCurrentSession(); + if (session != null) { + final MemoryViewDebugProcessData data = + DebuggerManager.getInstance(project).getDebugProcess(session.getDebugProcess().getProcessHandler()).getUserData( + MemoryViewDebugProcessData.KEY); + return data != null ? data.getTrackedStacks().getStack(ref) : null; + } + + return null; } } diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/action/tracking/TrackInstancesToggleAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/tracking/TrackInstancesToggleAction.java index c3131508ef49..0fc1f80bf86c 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/action/tracking/TrackInstancesToggleAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/action/tracking/TrackInstancesToggleAction.java @@ -20,7 +20,7 @@ import com.intellij.debugger.memory.tracking.TrackingType; import com.intellij.debugger.memory.ui.ClassesTable; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.ToggleAction; -import com.intellij.xdebugger.XDebugSession; +import com.intellij.openapi.project.Project; import com.sun.jdi.ArrayType; import com.sun.jdi.ReferenceType; import org.jetbrains.annotations.NotNull; @@ -32,7 +32,8 @@ public class TrackInstancesToggleAction extends ToggleAction { ReferenceType selectedClass = getSelectedClass(e); if (selectedClass instanceof ArrayType) { e.getPresentation().setEnabled(false); - } else { + } + else { super.update(e); } } @@ -40,9 +41,9 @@ public class TrackInstancesToggleAction extends ToggleAction { @Override public boolean isSelected(AnActionEvent e) { ReferenceType selectedClass = getSelectedClass(e); - XDebugSession debugSession = getDebugSession(e); - if (debugSession != null && selectedClass != null) { - InstancesTracker tracker = InstancesTracker.getInstance(debugSession.getProject()); + final Project project = e.getProject(); + if (project != null && selectedClass != null) { + InstancesTracker tracker = InstancesTracker.getInstance(project); return tracker.isTracked(selectedClass.name()); } @@ -51,10 +52,10 @@ public class TrackInstancesToggleAction extends ToggleAction { @Override public void setSelected(AnActionEvent e, boolean state) { - ReferenceType selectedClass = getSelectedClass(e); - XDebugSession debugSession = getDebugSession(e); - if (selectedClass != null && debugSession != null) { - InstancesTracker tracker = InstancesTracker.getInstance(debugSession.getProject()); + final ReferenceType selectedClass = getSelectedClass(e); + final Project project = e.getProject(); + if (selectedClass != null && project != null) { + InstancesTracker tracker = InstancesTracker.getInstance(project); boolean isAlreadyTracked = tracker.isTracked(selectedClass.name()); if (isAlreadyTracked && !state) { @@ -71,9 +72,4 @@ public class TrackInstancesToggleAction extends ToggleAction { private static ReferenceType getSelectedClass(AnActionEvent e) { return e.getData(ClassesTable.SELECTED_CLASS_KEY); } - - @Nullable - private static XDebugSession getDebugSession(AnActionEvent e) { - return e.getData(ClassesTable.DEBUG_SESSION_KEY); - } } diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/component/CreationPositionTracker.java b/java/debugger/impl/src/com/intellij/debugger/memory/component/CreationPositionTracker.java deleted file mode 100644 index e49668f18a7a..000000000000 --- a/java/debugger/impl/src/com/intellij/debugger/memory/component/CreationPositionTracker.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2000-2016 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.memory.component; - -import com.intellij.debugger.memory.utils.StackFrameItem; -import com.intellij.openapi.components.AbstractProjectComponent; -import com.intellij.openapi.project.Project; -import com.intellij.xdebugger.XDebugSession; -import com.intellij.xdebugger.XDebugSessionListener; -import com.sun.jdi.ObjectReference; -import com.sun.jdi.ReferenceType; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; - -public class CreationPositionTracker extends AbstractProjectComponent { - /** - * Stores all tracked instance for each debug session. - */ - private final ConcurrentHashMap>> - mySession2Reference2Stack = new ConcurrentHashMap<>(); - - private final ConcurrentHashMap>> - myPinnedSession2Reference2Stack = new ConcurrentHashMap<>(); - - public CreationPositionTracker(Project project) { - super(project); - } - - @Nullable - public static CreationPositionTracker getInstance(@NotNull Project project) { - return project.isDisposed() ? null : project.getComponent(CreationPositionTracker.class); - } - - @Nullable - public List getStack(@NotNull XDebugSession session, @NotNull ObjectReference ref) { - List stack = extract(mySession2Reference2Stack, session, ref); - return stack != null ? stack : extract(myPinnedSession2Reference2Stack, session, ref); - } - - public void addStack(@NotNull XDebugSession session, @NotNull ObjectReference ref, - @NotNull List stack) { - if (!mySession2Reference2Stack.containsKey(session)) { - mySession2Reference2Stack.put(session, new ConcurrentHashMap<>()); - session.addSessionListener(new XDebugSessionListener() { - @Override - public void sessionStopped() { - mySession2Reference2Stack.remove(session); - } - }); - } - - mySession2Reference2Stack.get(session).put(ref, stack); - } - - public void releaseBySession(@NotNull XDebugSession session) { - if(mySession2Reference2Stack.containsKey(session)) { - mySession2Reference2Stack.put(session, new ConcurrentHashMap<>()); - } - } - - public void unpinStacks(@NotNull XDebugSession session, @NotNull ReferenceType ref) { - Map> ref2Stack = myPinnedSession2Reference2Stack.getOrDefault(session, null); - if (ref2Stack != null) { - Iterator iterator = ref2Stack.keySet().iterator(); - while (iterator.hasNext()) { - ObjectReference reference = iterator.next(); - if (ref.equals(reference.referenceType())) { - iterator.remove(); - } - } - - if (ref2Stack.isEmpty()) { - myPinnedSession2Reference2Stack.remove(session); - } - } - } - - public void pinStacks(@NotNull XDebugSession session, @NotNull ReferenceType ref) { - Map> ref2Stack = mySession2Reference2Stack.getOrDefault(session, null); - if (ref2Stack != null) { - Map> ref2StacksByReferenceType = ref2Stack.entrySet().stream() - .filter(entry -> ref.equals(entry.getKey().referenceType())) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - myPinnedSession2Reference2Stack.put(session, ref2StacksByReferenceType); - } - } - - @Nullable - private static T extract(@NotNull Map> map, - @NotNull XDebugSession session, @NotNull ObjectReference ref) { - Map ref2something = map.getOrDefault(session, null); - if(ref2something != null) { - return ref2something.getOrDefault(ref, null); - } - - return null; - } -} diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/component/MemoryViewDebugProcessData.java b/java/debugger/impl/src/com/intellij/debugger/memory/component/MemoryViewDebugProcessData.java new file mode 100644 index 000000000000..8433e9ce1cb2 --- /dev/null +++ b/java/debugger/impl/src/com/intellij/debugger/memory/component/MemoryViewDebugProcessData.java @@ -0,0 +1,95 @@ +/* + * Copyright 2000-2016 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.memory.component; + +import com.intellij.debugger.memory.ui.ClassesFilteredView; +import com.intellij.debugger.memory.utils.StackFrameItem; +import com.intellij.openapi.util.Key; +import com.sun.jdi.ObjectReference; +import com.sun.jdi.ReferenceType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author Vitaliy.Bibaev + */ +public class MemoryViewDebugProcessData { + public static final Key KEY = Key.create("MemoryView.DebugProcessData"); + + private final TrackedStacksContainer myStacksContainer = new MyStackContainer(); + private final ClassesFilteredView myClassesFilteredView; + + public MemoryViewDebugProcessData(@NotNull ClassesFilteredView classesView) { + myClassesFilteredView = classesView; + } + + @NotNull + public ClassesFilteredView getClassesFilteredView() { + return myClassesFilteredView; + } + + @NotNull + public TrackedStacksContainer getTrackedStacks() { + return myStacksContainer; + } + + private static class MyStackContainer implements TrackedStacksContainer { + private final Map>> myType2Reference2Stack = new ConcurrentHashMap<>(); + + private final Map>> myPinnedType2Reference2Stack = new ConcurrentHashMap<>(); + + @Nullable + @Override + public List getStack(@NotNull ObjectReference reference) { + final List stack = extract(myType2Reference2Stack, reference); + return stack != null ? stack : extract(myPinnedType2Reference2Stack, reference); + } + + @Override + public void addStack(@NotNull ObjectReference ref, @NotNull List frames) { + myType2Reference2Stack.computeIfAbsent(ref.referenceType(), referenceType -> new ConcurrentHashMap<>()).put(ref, frames); + } + + @Override + public void pinStacks(@NotNull ReferenceType referenceType) { + final Map> ref2Stack = myType2Reference2Stack.get(referenceType); + if (ref2Stack != null) { + myPinnedType2Reference2Stack.put(referenceType, ref2Stack); + } + } + + @Override + public void unpinStacks(@NotNull ReferenceType referenceType) { + myPinnedType2Reference2Stack.remove(referenceType); + } + + @Override + public void release() { + myType2Reference2Stack.clear(); + } + + @Nullable + private static List extract(@NotNull Map>> map, + @NotNull ObjectReference ref) { + final Map> ref2Stack = map.get(ref.referenceType()); + return ref2Stack != null ? ref2Stack.get(ref) : null; + } + } +} diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/component/TrackedStacksContainer.java b/java/debugger/impl/src/com/intellij/debugger/memory/component/TrackedStacksContainer.java new file mode 100644 index 000000000000..b4e34b9e2a80 --- /dev/null +++ b/java/debugger/impl/src/com/intellij/debugger/memory/component/TrackedStacksContainer.java @@ -0,0 +1,40 @@ +/* + * Copyright 2000-2017 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.memory.component; + +import com.intellij.debugger.memory.utils.StackFrameItem; +import com.sun.jdi.ObjectReference; +import com.sun.jdi.ReferenceType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * @author Vitaliy.Bibaev + */ +public interface TrackedStacksContainer { + @Nullable + List getStack(@NotNull ObjectReference reference); + + void addStack(@NotNull ObjectReference ref, @NotNull List frames); + + void pinStacks(@NotNull ReferenceType referenceType); + + void unpinStacks(@NotNull ReferenceType referenceType); + + void release(); +} diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/toolwindow/MemoryViewToolWindowFactory.java b/java/debugger/impl/src/com/intellij/debugger/memory/toolwindow/MemoryViewToolWindowFactory.java index d760f28bbe37..7cfd031f01e6 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/toolwindow/MemoryViewToolWindowFactory.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/toolwindow/MemoryViewToolWindowFactory.java @@ -15,6 +15,11 @@ */ package com.intellij.debugger.memory.toolwindow; +import com.intellij.debugger.DebuggerManager; +import com.intellij.debugger.engine.DebugProcess; +import com.intellij.debugger.engine.DebugProcessImpl; +import com.intellij.debugger.engine.JavaDebugProcess; +import com.intellij.debugger.memory.component.MemoryViewDebugProcessData; import com.intellij.debugger.memory.component.MemoryViewManager; import com.intellij.debugger.memory.ui.ClassesFilteredView; import com.intellij.execution.Executor; @@ -35,6 +40,7 @@ import com.intellij.openapi.wm.ex.ToolWindowEx; import com.intellij.openapi.wm.ex.ToolWindowManagerAdapter; import com.intellij.openapi.wm.impl.ToolWindowImpl; import com.intellij.ui.components.JBLabel; +import com.intellij.util.EventDispatcher; import com.intellij.util.messages.MessageBusConnection; import com.intellij.xdebugger.XDebugProcess; import com.intellij.xdebugger.XDebugSession; @@ -46,8 +52,7 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; import java.util.Arrays; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; +import java.util.EventListener; public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware { private static final Logger LOG = Logger.getInstance(ClassesFilteredView.class); @@ -56,10 +61,9 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware private final JComponent myEmptyContent; private final JComponent myMemoryViewNotSupportedContent; - private final Map myMemoryViews = new ConcurrentHashMap<>(); + private final EventDispatcher myDispatcher = EventDispatcher.create(ToolWindowStateListener.class); - @Nullable - private ClassesFilteredView myCurrentView = null; + private volatile boolean myIsToolWindowVisible; { myEmptyContent = new JBLabel("Run debugging to see loaded classes", SwingConstants.CENTER); @@ -69,7 +73,7 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware @Override public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindow toolWindow) { - MessageBusConnection connection = project.getMessageBus().connect(project); + final MessageBusConnection connection = project.getMessageBus().connect(project); connection.subscribe(XDebuggerManager.TOPIC, new MyDebuggerStatusChangedListener()); connection.subscribe(RunContentManager.TOPIC, new RunContentWithExecutorListener() { @Override @@ -87,9 +91,11 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware .addToolWindowManagerListener(new ToolWindowManagerAdapter() { @Override public void stateChanged() { - if (!myMemoryViews.isEmpty() && !toolWindow.isDisposed()) { - myMemoryViews.values().forEach(classesFilteredView -> - classesFilteredView.setActive(toolWindow.isVisible())); + final boolean isVisible = toolWindow.isVisible(); + + if (isVisible != myIsToolWindowVisible) { + myDispatcher.getMulticaster().visibilityChanged(isVisible); + myIsToolWindowVisible = isVisible; } } }, project); @@ -100,63 +106,55 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware toolWindow.getComponent().setLayout(new BorderLayout()); for (XDebugSession session : XDebuggerManager.getInstance(project).getDebugSessions()) { - addNewSession(session); + registerMemoryView(session); } updateCurrentMemoryView(project, toolWindow); } - private void addNewSession(@NotNull XDebugSession session) { + private void registerMemoryView(@NotNull XDebugSession session) { LOG.assertTrue(SwingUtilities.isEventDispatchThread()); - ToolWindow toolWindow = getToolWindow(session.getProject()); - if (!myMemoryViews.containsKey(session)) { - try { - ClassesFilteredView newView = new ClassesFilteredView(session); - newView.setActive(toolWindow != null && toolWindow.isVisible()); - myMemoryViews.put(session, newView); - } - catch (Throwable e) { - LOG.warn("Cannot create new instance of the memory view. " + e.getMessage()); - } - } - } + final XDebugProcess debugProcess = session.getDebugProcess(); + final Project project = session.getProject(); + final DebugProcess javaProcess = + DebuggerManager.getInstance(session.getProject()).getDebugProcess(debugProcess.getProcessHandler()); + if (javaProcess instanceof DebugProcessImpl) { + final DebugProcessImpl processImpl = (DebugProcessImpl)javaProcess; + final ClassesFilteredView classesFilteredView = new ClassesFilteredView(debugProcess.getSession()); + myDispatcher.addListener(visible -> classesFilteredView.setActive(visible), classesFilteredView); - private void removeSession(@NotNull XDebugSession session) { - ClassesFilteredView removed = myMemoryViews.remove(session); - if (removed != null) { - Disposer.dispose(removed); + final MemoryViewDebugProcessData data = new MemoryViewDebugProcessData(classesFilteredView); + final ToolWindow toolWindow = getToolWindow(processImpl.getProject()); + + classesFilteredView.setActive(toolWindow != null && toolWindow.isVisible()); + processImpl.putUserData(MemoryViewDebugProcessData.KEY, data); + + if (toolWindow != null) { + updateCurrentMemoryView(project, toolWindow); + } } } private void updateCurrentMemoryView(@NotNull Project project, @NotNull ToolWindow toolWindow) { + JComponent component = myEmptyContent; if (!project.isDisposed()) { - XDebugSession session = XDebuggerManager.getInstance(project).getCurrentSession(); + final XDebugSession session = XDebuggerManager.getInstance(project).getCurrentSession(); + if (session != null) { - ClassesFilteredView view; - if (myMemoryViews.containsKey(session)) { - view = myMemoryViews.get(session); - replaceToolWindowContent(toolWindow, view); - } - else { - view = null; - replaceToolWindowContent(toolWindow, myMemoryViewNotSupportedContent); - } + final DebugProcess debugProcess = DebuggerManager.getInstance(project) + .getDebugProcess(session.getDebugProcess().getProcessHandler()); + final MemoryViewDebugProcessData data = debugProcess != null ? debugProcess.getUserData(MemoryViewDebugProcessData.KEY) : null; - if (myCurrentView != null) { - myCurrentView.setActive(false); - } - - myCurrentView = view; - return; + component = data != null ? data.getClassesFilteredView() : myMemoryViewNotSupportedContent; } } - replaceToolWindowContent(toolWindow, myEmptyContent); + replaceToolWindowContent(toolWindow, component); } private static void replaceToolWindowContent(@NotNull ToolWindow toolWindow, JComponent comp) { LOG.assertTrue(SwingUtilities.isEventDispatchThread()); - JComponent toolWindowComp = toolWindow.getComponent(); + final JComponent toolWindowComp = toolWindow.getComponent(); toolWindowComp.removeAll(); toolWindowComp.add(comp); toolWindowComp.repaint(); @@ -170,7 +168,7 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware public static class Condition implements com.intellij.openapi.util.Condition { @Override public boolean value(Project project) { - MessageBusConnection connection = project.getMessageBus().connect(project); + final MessageBusConnection connection = project.getMessageBus().connect(project); connection.subscribe(XDebuggerManager.TOPIC, new XDebuggerManagerListener() { @Override public void processStarted(@NotNull XDebugProcess debugProcess) { @@ -179,16 +177,16 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware @Override public void processStopped(@NotNull XDebugProcess debugProcess) { - Project project = debugProcess.getSession().getProject(); - boolean enabled = Arrays.stream(XDebuggerManager.getInstance(project) - .getDebugSessions()).anyMatch(session -> !session.getDebugProcess().equals(debugProcess)); + final Project project = debugProcess.getSession().getProject(); + final boolean enabled = Arrays.stream(XDebuggerManager.getInstance(project) + .getDebugSessions()).anyMatch(session -> !session.getDebugProcess().equals(debugProcess)); updateIcon(project, enabled); } private void updateIcon(@NotNull Project project, boolean enabled) { - ToolWindow toolWindow = MemoryViewManager.getInstance().getToolWindow(project); + final ToolWindow toolWindow = MemoryViewManager.getInstance().getToolWindow(project); if (toolWindow != null) { - Icon icon = enabled ? AllIcons.Debugger.MemoryView.ToolWindowEnabled : AllIcons.Debugger.MemoryView.ToolWindowDisabled; + final Icon icon = enabled ? AllIcons.Debugger.MemoryView.ToolWindowEnabled : AllIcons.Debugger.MemoryView.ToolWindowDisabled; ApplicationManager.getApplication().invokeLater(() -> toolWindow.setIcon(icon)); } } @@ -200,28 +198,36 @@ public class MemoryViewToolWindowFactory implements ToolWindowFactory, DumbAware private final class MyDebuggerStatusChangedListener implements XDebuggerManagerListener { @Override public void processStarted(@NotNull XDebugProcess xDebugProcess) { - ApplicationManager.getApplication().invokeLater(() -> { - XDebugSession session = xDebugProcess.getSession(); - addNewSession(session); - updateView(session); - }); + ApplicationManager.getApplication().invokeLater(() -> registerMemoryView(xDebugProcess.getSession())); } @Override public void processStopped(@NotNull XDebugProcess xDebugProcess) { - XDebugSession session = xDebugProcess.getSession(); - removeSession(session); + final XDebugSession session = xDebugProcess.getSession(); + if (xDebugProcess instanceof JavaDebugProcess) { + final DebugProcessImpl process = ((JavaDebugProcess)xDebugProcess).getDebuggerSession().getProcess(); + + final MemoryViewDebugProcessData data = process.getUserData(MemoryViewDebugProcessData.KEY); + if (data != null) { + Disposer.dispose(data.getClassesFilteredView()); + } + } + ApplicationManager.getApplication().invokeLater(() -> updateView(session)); } private void updateView(@NotNull XDebugSession debugSession) { - Project project = debugSession.getProject(); + final Project project = debugSession.getProject(); if (!project.isDisposed()) { - ToolWindow toolWindow = getToolWindow(project); + final ToolWindow toolWindow = getToolWindow(project); if (toolWindow != null) { updateCurrentMemoryView(project, toolWindow); } } } } + + private interface ToolWindowStateListener extends EventListener { + void visibilityChanged(boolean visible); + } } diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/tracking/ClassPreparedListener.java b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/ClassPreparedListener.java index 411e86e51d95..b591fdc4ff19 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/tracking/ClassPreparedListener.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/ClassPreparedListener.java @@ -19,24 +19,20 @@ import com.intellij.debugger.DebuggerManager; import com.intellij.debugger.engine.DebugProcess; import com.intellij.debugger.engine.DebugProcessImpl; import com.intellij.debugger.requests.ClassPrepareRequestor; -import com.intellij.openapi.project.Project; import com.intellij.xdebugger.XDebugSession; import com.sun.jdi.ReferenceType; import com.sun.jdi.request.ClassPrepareRequest; import org.jetbrains.annotations.NotNull; public abstract class ClassPreparedListener { - private final XDebugSession myDebugSession; - private final Project myProject; protected ClassPreparedListener(@NotNull String className, @NotNull XDebugSession debugSession) { - myProject = debugSession.getProject(); - myDebugSession = debugSession; - DebugProcessImpl debugProcess = getDebugProcess(); - ClassPrepareRequestor request = new MyClassPreparedRequest(); - ClassPrepareRequest classPrepareRequest = debugProcess.getRequestsManager() - .createClassPrepareRequest(request, className); + final DebugProcessImpl debugProcess = (DebugProcessImpl)DebuggerManager.getInstance(debugSession.getProject()) + .getDebugProcess(debugSession.getDebugProcess().getProcessHandler()); + final ClassPrepareRequestor request = new MyClassPreparedRequest(); + final ClassPrepareRequest classPrepareRequest = debugProcess.getRequestsManager() + .createClassPrepareRequest(request, className); if (classPrepareRequest != null) { classPrepareRequest.enable(); } @@ -44,16 +40,13 @@ public abstract class ClassPreparedListener { public abstract void onClassPrepared(@NotNull ReferenceType referenceType); - private DebugProcessImpl getDebugProcess() { - return (DebugProcessImpl) DebuggerManager.getInstance(myProject) - .getDebugProcess(myDebugSession.getDebugProcess().getProcessHandler()); - } - private final class MyClassPreparedRequest implements ClassPrepareRequestor { @Override public void processClassPrepare(DebugProcess debuggerProcess, ReferenceType referenceType) { - getDebugProcess().getRequestsManager().deleteRequest(this); - onClassPrepared(referenceType); + if (debuggerProcess instanceof DebugProcessImpl) { + ((DebugProcessImpl)debuggerProcess).getRequestsManager().deleteRequest(this); + onClassPrepared(referenceType); + } } } } diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/tracking/ConstructorInstancesTracker.java b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/ConstructorInstancesTracker.java index cb72cd539d15..549da0518f93 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/tracking/ConstructorInstancesTracker.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/tracking/ConstructorInstancesTracker.java @@ -16,13 +16,14 @@ package com.intellij.debugger.memory.tracking; import com.intellij.debugger.DebuggerManager; +import com.intellij.debugger.engine.DebugProcess; import com.intellij.debugger.engine.DebugProcessImpl; import com.intellij.debugger.engine.SuspendContextImpl; import com.intellij.debugger.engine.evaluation.EvaluateException; import com.intellij.debugger.engine.events.DebuggerCommandImpl; import com.intellij.debugger.engine.events.SuspendContextCommandImpl; -import com.intellij.debugger.memory.component.CreationPositionTracker; import com.intellij.debugger.memory.component.InstancesTracker; +import com.intellij.debugger.memory.component.MemoryViewDebugProcessData; import com.intellij.debugger.memory.event.InstancesTrackerListener; import com.intellij.debugger.memory.utils.StackFrameItem; import com.intellij.debugger.ui.breakpoints.JavaLineBreakpointType; @@ -55,9 +56,7 @@ import java.util.List; public class ConstructorInstancesTracker implements TrackerForNewInstances, Disposable, BackgroundTracker { private static final int TRACKED_INSTANCES_LIMIT = 2000; private final ReferenceType myReference; - private final XDebugSession myDebugSession; - private final CreationPositionTracker myPositionTracker; - private final DebugProcessImpl myDebugProcess; + private final Project myProject; private final MyConstructorBreakpoints myBreakpoint; @Nullable @@ -72,18 +71,19 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp public ConstructorInstancesTracker(@NotNull ReferenceType ref, @NotNull XDebugSession debugSession) { myReference = ref; - myDebugSession = debugSession; - myPositionTracker = CreationPositionTracker.getInstance(debugSession.getProject()); - Project project = debugSession.getProject(); - myIsBackgroundTrackingEnabled = InstancesTracker.getInstance(myDebugSession.getProject()) + myProject = debugSession.getProject(); + myIsBackgroundTrackingEnabled = InstancesTracker.getInstance(myProject) .isBackgroundTrackingEnabled(); - InstancesTracker.getInstance(myDebugSession.getProject()).addTrackerListener(new InstancesTrackerListener() { + final DebugProcessImpl debugProcess = (DebugProcessImpl)DebuggerManager.getInstance(myProject) + .getDebugProcess(debugSession.getDebugProcess().getProcessHandler()); + + InstancesTracker.getInstance(myProject).addTrackerListener(new InstancesTrackerListener() { @Override public void backgroundTrackingValueChanged(boolean newState) { if (myIsBackgroundTrackingEnabled != newState) { myIsBackgroundTrackingEnabled = newState; - myDebugProcess.getManagerThread().schedule(new DebuggerCommandImpl() { + debugProcess.getManagerThread().schedule(new DebuggerCommandImpl() { @Override protected void action() throws Exception { if (newState) { @@ -98,17 +98,24 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp } }, this); - myDebugProcess = (DebugProcessImpl)DebuggerManager.getInstance(project) - .getDebugProcess(debugSession.getDebugProcess().getProcessHandler()); - JavaLineBreakpointType breakPointType = new JavaLineBreakpointType(); + final JavaLineBreakpointType breakPointType = new JavaLineBreakpointType(); - XBreakpoint bpn = new XLineBreakpointImpl<>(breakPointType, - ((XDebuggerManagerImpl)XDebuggerManager.getInstance(project)).getBreakpointManager(), - new JavaLineBreakpointProperties(), - new LineBreakpointState<>()); + final XBreakpoint bpn = new XLineBreakpointImpl<>(breakPointType, + ((XDebuggerManagerImpl)XDebuggerManager.getInstance(myProject)) + .getBreakpointManager(), + new JavaLineBreakpointProperties(), + new LineBreakpointState<>()); - myBreakpoint = new MyConstructorBreakpoints(project, bpn); - myBreakpoint.createRequestForPreparedClass(myDebugProcess, myReference); + myBreakpoint = new MyConstructorBreakpoints(myProject, bpn); + myBreakpoint.createRequestForPreparedClass(debugProcess, myReference); + Disposer.register(myBreakpoint, () -> debugProcess.getManagerThread().schedule(new DebuggerCommandImpl() { + @Override + protected void action() throws Exception { + disable(); + debugProcess.getRequestsManager().deleteRequest(myBreakpoint); + myBreakpoint.delete(); + } + })); } public void obsolete() { @@ -120,7 +127,15 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp if (!myIsBackgroundMode || myIsBackgroundTrackingEnabled) { myBreakpoint.enable(); } - myPositionTracker.releaseBySession(myDebugSession); + + final XDebugSession session = XDebuggerManager.getInstance(myProject).getCurrentSession(); + if (session != null) { + final DebugProcess process = DebuggerManager.getInstance(myProject).getDebugProcess(session.getDebugProcess().getProcessHandler()); + final MemoryViewDebugProcessData data = process.getUserData(MemoryViewDebugProcessData.KEY); + if (data != null) { + data.getTrackedStacks().release(); + } + } } public void commitTracked() { @@ -183,8 +198,8 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp } private final class MyConstructorBreakpoints extends LineBreakpoint implements Disposable { - private boolean myIsEnabled = false; private final List myRequests = new ArrayList<>(); + private volatile boolean myIsEnabled = false; private volatile boolean myIsDeleted = false; MyConstructorBreakpoints(Project project, XBreakpoint xBreakpoint) { @@ -209,16 +224,12 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp public void reload() { } + void delete() { + myIsDeleted = true; + } + @Override public void dispose() { - myDebugProcess.getManagerThread().schedule(new DebuggerCommandImpl() { - @Override - protected void action() throws Exception { - disable(); - myDebugProcess.getRequestsManager().deleteRequest(MyConstructorBreakpoints.this); - myIsDeleted = true; - } - }); } @Override @@ -227,11 +238,13 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp try { SuspendContextImpl suspendContext = action.getSuspendContext(); if (suspendContext != null) { + final MemoryViewDebugProcessData data = suspendContext.getDebugProcess().getUserData(MemoryViewDebugProcessData.KEY); ObjectReference thisRef = getThisObject(suspendContext, event); - if (myReference.equals(thisRef.referenceType()) && myPositionTracker != null) { + if (myReference.equals(thisRef.referenceType()) && data != null) { thisRef.disableCollection(); myTrackedObjects.add(thisRef); - myPositionTracker.addStack(myDebugSession, thisRef, StackFrameItem.createFrames(suspendContext.getThread())); + final List frame = StackFrameItem.createFrames(suspendContext.getThread()); + data.getTrackedStacks().addStack(thisRef, frame); } } } diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/ui/ClassesFilteredView.java b/java/debugger/impl/src/com/intellij/debugger/memory/ui/ClassesFilteredView.java index f25d6b714405..721a12db0a45 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/ui/ClassesFilteredView.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/ui/ClassesFilteredView.java @@ -17,10 +17,18 @@ package com.intellij.debugger.memory.ui; import com.intellij.debugger.DebuggerManager; import com.intellij.debugger.DebuggerManagerEx; +import com.intellij.debugger.engine.DebugProcess; import com.intellij.debugger.engine.DebugProcessImpl; import com.intellij.debugger.engine.SuspendContextImpl; import com.intellij.debugger.engine.events.DebuggerCommandImpl; +import com.intellij.debugger.memory.component.*; +import com.intellij.debugger.memory.event.InstancesTrackerListener; import com.intellij.debugger.memory.event.MemoryViewManagerListener; +import com.intellij.debugger.memory.tracking.ClassPreparedListener; +import com.intellij.debugger.memory.tracking.ConstructorInstancesTracker; +import com.intellij.debugger.memory.tracking.TrackerForNewInstances; +import com.intellij.debugger.memory.tracking.TrackingType; +import com.intellij.debugger.memory.utils.AndroidUtil; import com.intellij.debugger.memory.utils.KeyboardUtils; import com.intellij.debugger.memory.utils.LowestPriorityCommand; import com.intellij.debugger.memory.utils.SingleAlarmWithMutableDelay; @@ -37,21 +45,12 @@ import com.intellij.util.SmartList; import com.intellij.util.ui.components.BorderLayoutPanel; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebugSessionListener; +import com.intellij.xdebugger.XDebuggerManager; import com.sun.jdi.ObjectReference; import com.sun.jdi.ReferenceType; import com.sun.jdi.VirtualMachine; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import com.intellij.debugger.memory.component.CreationPositionTracker; -import com.intellij.debugger.memory.component.InstancesTracker; -import com.intellij.debugger.memory.component.MemoryViewManager; -import com.intellij.debugger.memory.component.MemoryViewManagerState; -import com.intellij.debugger.memory.event.InstancesTrackerListener; -import com.intellij.debugger.memory.tracking.ClassPreparedListener; -import com.intellij.debugger.memory.tracking.ConstructorInstancesTracker; -import com.intellij.debugger.memory.tracking.TrackerForNewInstances; -import com.intellij.debugger.memory.tracking.TrackingType; -import com.intellij.debugger.memory.utils.AndroidUtil; import javax.swing.FocusManager; import javax.swing.*; @@ -63,6 +62,7 @@ import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.IntStream; import static com.intellij.debugger.memory.ui.ClassesTable.DiffViewTableModel.CLASSNAME_COLUMN_INDEX; @@ -77,15 +77,13 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable private static final String EMPTY_TABLE_CONTENT_WHEN_SUSPENDED = "Nothing to show"; private final Project myProject; - private final XDebugSession myDebugSession; - private final DebugProcessImpl myDebugProcess; private final SingleAlarmWithMutableDelay mySingleAlarm; private final SearchTextField myFilterTextField = new FilterTextField(); private final ClassesTable myTable; private final InstancesTracker myInstancesTracker; private final Map myConstructorTrackedClasses = new ConcurrentHashMap<>(); - private final XDebugSessionListener myDebugSessionListener; + private final MyDebuggerSessionListener myDebugSessionListener; @Nullable private volatile SuspendContextImpl myLastSuspendContext; @@ -95,7 +93,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable *

* State: false to true */ - private volatile boolean myIsTrackersActivated = false; + private final AtomicBoolean myIsTrackersActivated = new AtomicBoolean(false); /** * Indicates that view is visible in tool window @@ -106,27 +104,27 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable super(); myProject = debugSession.getProject(); - myDebugSession = debugSession; - myDebugProcess = (DebugProcessImpl) DebuggerManager.getInstance(myProject) - .getDebugProcess(myDebugSession.getDebugProcess().getProcessHandler()); + final DebugProcessImpl debugProcess = (DebugProcessImpl)DebuggerManager.getInstance(myProject) + .getDebugProcess(debugSession.getDebugProcess().getProcessHandler()); - if (myDebugProcess == null) { + if (debugProcess == null) { throw new NullPointerException("Failed to receive a java debug process"); } myInstancesTracker = InstancesTracker.getInstance(myProject); - InstancesTrackerListener instancesTrackerListener = new InstancesTrackerListener() { + final InstancesTrackerListener instancesTrackerListener = new InstancesTrackerListener() { @Override public void classChanged(@NotNull String name, @NotNull TrackingType type) { ReferenceType ref = myTable.getClassByName(name); if (ref != null) { - myDebugProcess.getManagerThread() - .schedule(new LowestPriorityCommand(getSuspendContext()) { - @Override - public void contextAction(@NotNull SuspendContextImpl suspendContext) throws Exception { - trackClass(ref, type, myIsTrackersActivated); - } - }); + final boolean activated = myIsTrackersActivated.get(); + debugProcess.getManagerThread() + .schedule(new LowestPriorityCommand(getSuspendContext()) { + @Override + public void contextAction(@NotNull SuspendContextImpl suspendContext) throws Exception { + trackClass(ref, type, activated); + } + }); } myTable.repaint(); } @@ -142,7 +140,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable } }; - myDebugProcess.getManagerThread().schedule(new DebuggerCommandImpl() { + debugProcess.getManagerThread().schedule(new DebuggerCommandImpl() { @Override public Priority getPriority() { return Priority.LOWEST; @@ -150,20 +148,22 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable @Override protected void action() throws Exception { + final boolean activated = myIsTrackersActivated.get(); for (Map.Entry entry : myInstancesTracker.getTrackedClasses().entrySet()) { TrackingType type = entry.getValue(); String className = entry.getKey(); - List classes = myDebugProcess.getVirtualMachineProxy().classesByName(className); + List classes = debugProcess.getVirtualMachineProxy().classesByName(className); if (classes.isEmpty()) { - new ClassPreparedListener(className, myDebugSession) { + new ClassPreparedListener(className, debugSession) { @Override public void onClassPrepared(@NotNull ReferenceType referenceType) { - trackClass(referenceType, type, myIsTrackersActivated); + trackClass(referenceType, type, activated); } }; - } else { + } + else { for (ReferenceType ref : classes) { - trackClass(ref, type, myIsTrackersActivated); + trackClass(ref, type, activated); } } } @@ -172,10 +172,10 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable } }); - MemoryViewManagerState memoryViewManagerState = MemoryViewManager.getInstance().getState(); + final MemoryViewManagerState memoryViewManagerState = MemoryViewManager.getInstance().getState(); - myTable = new ClassesTable(myDebugSession, memoryViewManagerState.isShowWithDiffOnly, - memoryViewManagerState.isShowWithInstancesOnly, memoryViewManagerState.isShowTrackedOnly, this); + myTable = new ClassesTable(debugSession, memoryViewManagerState.isShowWithDiffOnly, + memoryViewManagerState.isShowWithInstancesOnly, memoryViewManagerState.isShowTrackedOnly, this); myTable.getEmptyText().setText(EMPTY_TABLE_CONTENT_WHEN_RUNNING); Disposer.register(this, myTable); @@ -186,14 +186,15 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable myTable.addKeyListener(new KeyAdapter() { @Override public void keyReleased(KeyEvent e) { - int keyCode = e.getKeyCode(); + final int keyCode = e.getKeyCode(); if (KeyboardUtils.isEnterKey(keyCode)) { handleClassSelection(myTable.getSelectedClass()); - } else if (KeyboardUtils.isCharacter(keyCode) || KeyboardUtils.isBackSpace(keyCode)) { - String text = myFilterTextField.getText(); - String newText = KeyboardUtils.isBackSpace(keyCode) - ? text.substring(0, text.length() - 1) - : text + e.getKeyChar(); + } + else if (KeyboardUtils.isCharacter(keyCode) || KeyboardUtils.isBackSpace(keyCode)) { + final String text = myFilterTextField.getText(); + final String newText = KeyboardUtils.isBackSpace(keyCode) + ? text.substring(0, text.length() - 1) + : text + e.getKeyChar(); myFilterTextField.setText(newText); FocusManager.getCurrentManager().focusNextComponent(myFilterTextField); } @@ -233,35 +234,15 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable MemoryViewManager.getInstance().addMemoryViewManagerListener(memoryViewManagerListener, this); - myDebugSessionListener = new XDebugSessionListener() { - @Override - public void sessionResumed() { - myConstructorTrackedClasses.values().forEach(ConstructorInstancesTracker::obsolete); - ApplicationManager.getApplication().invokeLater(() -> { - myTable.getEmptyText().setText(EMPTY_TABLE_CONTENT_WHEN_RUNNING); - myTable.hideContent(); - }); - mySingleAlarm.cancelAllRequests(); - } - - @Override - public void sessionStopped() { - debugSession.removeSessionListener(this); - } - - @Override - public void sessionPaused() { - ApplicationManager.getApplication().invokeLater(() -> myTable.getEmptyText().setText(EMPTY_TABLE_CONTENT_WHEN_SUSPENDED)); - updateClassesAndCounts(); - } - }; + myDebugSessionListener = new MyDebuggerSessionListener(); + debugSession.addSessionListener(myDebugSessionListener); mySingleAlarm = new SingleAlarmWithMutableDelay(() -> { myLastSuspendContext = getSuspendContext(); if (myLastSuspendContext != null) { ApplicationManager.getApplication().invokeLater(() -> myTable.setBusy(true)); - myDebugProcess.getManagerThread() - .schedule(new MyUpdateClassesCommand(myLastSuspendContext)); + debugProcess.getManagerThread() + .schedule(new MyUpdateClassesCommand(myLastSuspendContext)); } }, this); @@ -275,7 +256,7 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable } }); - JScrollPane scroll = ScrollPaneFactory.createScrollPane(myTable, SideBorder.TOP); + final JScrollPane scroll = ScrollPaneFactory.createScrollPane(myTable, SideBorder.TOP); addToTop(myFilterTextField); addToCenter(scroll); } @@ -290,16 +271,22 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable boolean isTrackerEnabled) { LOG.assertTrue(DebuggerManager.getInstance(myProject).isDebuggerManagerThread()); if (type == TrackingType.CREATION) { - ConstructorInstancesTracker old = myConstructorTrackedClasses.getOrDefault(ref, null); + final ConstructorInstancesTracker old = myConstructorTrackedClasses.getOrDefault(ref, null); if (old != null) { Disposer.dispose(old); } - ConstructorInstancesTracker tracker = new ConstructorInstancesTracker(ref, myDebugSession); + final XDebugSession debugSession = XDebuggerManager.getInstance(myProject).getCurrentSession(); + if (debugSession == null) { + return; + } + + final ConstructorInstancesTracker tracker = new ConstructorInstancesTracker(ref, debugSession); tracker.setBackgroundMode(!myIsActive); if (isTrackerEnabled) { tracker.enable(); - } else { + } + else { tracker.disable(); } @@ -309,8 +296,9 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable } private void handleClassSelection(@Nullable ReferenceType ref) { - if (ref != null && myDebugSession.isSuspended()) { - new InstancesWindow(myDebugSession, ref::instances, ref.name()).show(); + final XDebugSession debugSession = XDebuggerManager.getInstance(myProject).getCurrentSession(); + if (ref != null && debugSession != null && debugSession.isSuspended()) { + new InstancesWindow(debugSession, limit -> ref.instances(limit), ref.name()).show(); } } @@ -323,18 +311,24 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable } private void updateClassesAndCounts() { - if (myDebugProcess.isAttached()) { - mySingleAlarm.cancelAndRequest(); + final XDebugSession debugSession = XDebuggerManager.getInstance(myProject).getCurrentSession(); + if (debugSession != null) { + final DebugProcess debugProcess = DebuggerManager.getInstance(myProject) + .getDebugProcess(debugSession.getDebugProcess().getProcessHandler()); + if (debugProcess.isAttached()) { + mySingleAlarm.cancelAndRequest(); + } } } private static ActionPopupMenu createContextMenu() { - ActionGroup group = (ActionGroup) ActionManager.getInstance().getAction("MemoryView.ClassesPopupActionGroup"); + final ActionGroup group = (ActionGroup)ActionManager.getInstance().getAction("MemoryView.ClassesPopupActionGroup"); return ActionManager.getInstance().createActionPopupMenu("MemoryView.ClassesPopupActionGroup", group); } @Override public void dispose() { + myLastSuspendContext = null; } public void setActive(boolean active) { @@ -343,29 +337,36 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable } myIsActive = active; - myDebugProcess.getManagerThread().schedule(new DebuggerCommandImpl() { - @Override - protected void action() throws Exception { - if (active) { - doActivate(); - } else { - doPause(); + final XDebugSession debugSession = XDebuggerManager.getInstance(myProject).getCurrentSession(); + if (debugSession != null) { + final DebugProcessImpl debugProcess = + (DebugProcessImpl)DebuggerManager.getInstance(myProject).getDebugProcess(debugSession.getDebugProcess().getProcessHandler()); + debugProcess.getManagerThread().schedule(new DebuggerCommandImpl() { + @Override + protected void action() throws Exception { + if (active) { + doActivate(); + } + else { + doPause(); + } } - } - }); + }); + } } private void doActivate() { - myDebugSession.addSessionListener(myDebugSessionListener, this); + myDebugSessionListener.setActive(true); myConstructorTrackedClasses.values().forEach(x -> x.setBackgroundMode(false)); final SuspendContextImpl lastContext = myLastSuspendContext; + if (lastContext == null || !lastContext.equals(getSuspendContext())) { updateClassesAndCounts(); } } private void doPause() { - myDebugSession.removeSessionListener(myDebugSessionListener); + myDebugSessionListener.setActive(false); myConstructorTrackedClasses.values().forEach(x -> x.setBackgroundMode(true)); } @@ -377,45 +378,47 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable @Override public void contextAction(@NotNull SuspendContextImpl suspendContext) throws Exception { - if (!myIsTrackersActivated) { + if (!myIsTrackersActivated.get()) { myConstructorTrackedClasses.values().forEach(ConstructorInstancesTracker::enable); - myIsTrackersActivated = true; - } else { + myIsTrackersActivated.set(true); + } + else { commitAllTrackers(); } - final List classes = myDebugProcess.getVirtualMachineProxy().allClasses(); + + final List classes = suspendContext.getDebugProcess().getVirtualMachineProxy().allClasses(); if (classes.isEmpty()) { return; } - VirtualMachine vm = classes.get(0).virtualMachine(); - int batchSize = AndroidUtil.isAndroidVM(vm) - ? AndroidUtil.ANDROID_COUNT_BY_CLASSES_BATCH_SIZE - : DEFAULT_BATCH_SIZE; + final VirtualMachine vm = classes.get(0).virtualMachine(); + final int batchSize = AndroidUtil.isAndroidVM(vm) + ? AndroidUtil.ANDROID_COUNT_BY_CLASSES_BATCH_SIZE + : DEFAULT_BATCH_SIZE; - List chunks = new SmartList<>(); - int size = classes.size(); + final List chunks = new SmartList<>(); + final int size = classes.size(); for (int begin = 0, end = Math.min(batchSize, size); begin != size && isContextValid(); begin = end, end = Math.min(end + batchSize, size)) { - List batch = classes.subList(begin, end); + final List batch = classes.subList(begin, end); - long start = System.nanoTime(); - long[] counts = vm.instanceCounts(batch); - long delay = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); + final long start = System.nanoTime(); + final long[] counts = vm.instanceCounts(batch); + final long delay = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); chunks.add(counts); - int waitTime = (int) Math.min(DELAY_BEFORE_INSTANCES_QUERY_COEFFICIENT * delay, MAX_DELAY_MILLIS); + final int waitTime = (int)Math.min(DELAY_BEFORE_INSTANCES_QUERY_COEFFICIENT * delay, MAX_DELAY_MILLIS); mySingleAlarm.setDelay(waitTime); LOG.info(String.format("Instances query time = %d ms. Count = %d", delay, batch.size())); } if (isContextValid()) { final long[] counts = chunks.size() == 1 ? chunks.get(0) : IntStream.range(0, chunks.size()).boxed() - .flatMapToLong(integer -> Arrays.stream(chunks.get(integer))) - .toArray(); + .flatMapToLong(integer -> Arrays.stream(chunks.get(integer))) + .toArray(); ApplicationManager.getApplication().invokeLater(() -> myTable.setClassesAndUpdateCounts(classes, counts)); } @@ -449,15 +452,23 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable return; } - final CreationPositionTracker tracker = CreationPositionTracker.getInstance(myDebugSession.getProject()); - ReferenceType ref = myTable.getSelectedClass(); - TrackerForNewInstances strategy = ref == null ? null : getStrategy(ref); - if (strategy != null && tracker != null) { - List newInstances = strategy.getNewInstances(); - tracker.pinStacks(myDebugSession, ref); - InstancesWindow instancesWindow = new InstancesWindow(myDebugSession, limit -> newInstances, ref.name()); - Disposer.register(instancesWindow.getDisposable(), () -> tracker.unpinStacks(myDebugSession, ref)); - instancesWindow.show(); + final ReferenceType ref = myTable.getSelectedClass(); + final TrackerForNewInstances strategy = ref == null ? null : getStrategy(ref); + XDebugSession debugSession = XDebuggerManager.getInstance(myProject).getCurrentSession(); + if (strategy != null && debugSession != null) { + final DebugProcess debugProcess = + DebuggerManager.getInstance(myProject).getDebugProcess(debugSession.getDebugProcess().getProcessHandler()); + final MemoryViewDebugProcessData data = debugProcess.getUserData(MemoryViewDebugProcessData.KEY); + if (data != null) { + final List newInstances = strategy.getNewInstances(); + data.getTrackedStacks().pinStacks(ref); + final InstancesWindow instancesWindow = new InstancesWindow(debugSession, limit -> newInstances, ref.name()); + Disposer.register(instancesWindow.getDisposable(), () -> data.getTrackedStacks().unpinStacks(ref)); + instancesWindow.show(); + } + else { + LOG.warn("MemoryViewDebugProcessData not found in debug session user data"); + } } } } @@ -483,7 +494,8 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable public void mouseMoved(MouseEvent e) { if (isShowNewInstancesEvent(e)) { myTable.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - } else { + } + else { myTable.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); } } @@ -498,9 +510,37 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable final int modelRow = myTable.convertRowIndexToModel(row); - final ReferenceType ref = (ReferenceType) myTable.getModel().getValueAt(modelRow, CLASSNAME_COLUMN_INDEX); + final ReferenceType ref = (ReferenceType)myTable.getModel().getValueAt(modelRow, CLASSNAME_COLUMN_INDEX); final ConstructorInstancesTracker tracker = myConstructorTrackedClasses.getOrDefault(ref, null); return tracker != null && tracker.isReady() && tracker.getCount() > 0; } + + private class MyDebuggerSessionListener implements XDebugSessionListener { + private volatile boolean myIsActive = false; + + void setActive(boolean value) { + myIsActive = value; + } + + @Override + public void sessionResumed() { + if (myIsActive) { + myConstructorTrackedClasses.values().forEach(ConstructorInstancesTracker::obsolete); + ApplicationManager.getApplication().invokeLater(() -> { + myTable.getEmptyText().setText(EMPTY_TABLE_CONTENT_WHEN_RUNNING); + myTable.hideContent(); + }); + mySingleAlarm.cancelAllRequests(); + } + } + + @Override + public void sessionPaused() { + if (myIsActive) { + ApplicationManager.getApplication().invokeLater(() -> myTable.getEmptyText().setText(EMPTY_TABLE_CONTENT_WHEN_SUSPENDED)); + updateClassesAndCounts(); + } + } + } } diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/ui/ClassesTable.java b/java/debugger/impl/src/com/intellij/debugger/memory/ui/ClassesTable.java index 6c4bcb9cbb15..28d02623df65 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/ui/ClassesTable.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/ui/ClassesTable.java @@ -16,8 +16,11 @@ package com.intellij.debugger.memory.ui; import com.intellij.debugger.memory.component.InstancesTracker; +import com.intellij.debugger.memory.tracking.TrackerForNewInstances; +import com.intellij.debugger.memory.tracking.TrackingType; import com.intellij.debugger.memory.utils.AbstractTableColumnDescriptor; import com.intellij.debugger.memory.utils.AbstractTableModelWithColumns; +import com.intellij.debugger.memory.utils.InstancesProvider; import com.intellij.icons.AllIcons; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.DataKey; @@ -39,9 +42,6 @@ import com.sun.jdi.ReferenceType; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import com.intellij.debugger.memory.tracking.TrackerForNewInstances; -import com.intellij.debugger.memory.tracking.TrackingType; -import com.intellij.debugger.memory.utils.InstancesProvider; import javax.swing.*; import javax.swing.border.Border; @@ -52,7 +52,6 @@ import java.util.concurrent.ConcurrentHashMap; public class ClassesTable extends JBTable implements DataProvider, Disposable { public static final DataKey SELECTED_CLASS_KEY = DataKey.create("ClassesTable.SelectedClass"); - public static final DataKey DEBUG_SESSION_KEY = DataKey.create("ClassesTable.DebugSession"); public static final DataKey NEW_INSTANCES_PROVIDER_KEY = DataKey.create("ClassesTable.NewInstances"); public static final DataKey REF_COUNT_PROVIDER_KEY = @@ -63,10 +62,9 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { private static final int CLASSES_COLUMN_PREFERRED_WIDTH = 250; private static final int COUNT_COLUMN_MIN_WIDTH = 80; private static final int DIFF_COLUMN_MIN_WIDTH = 80; + private static final UnknownDiffValue UNKNOWN_VALUE = new UnknownDiffValue(); private final DiffViewTableModel myModel = new DiffViewTableModel(); - private final UnknownDiffValue myUnknownValue = new UnknownDiffValue(); - private final XDebugSession myDebugSession; private final Map myCounts = new ConcurrentHashMap<>(); private final InstancesTracker myInstancesTracker; private final ClassesFilteredView myParent; @@ -84,11 +82,10 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { boolean onlyTracked, @NotNull ClassesFilteredView parent) { setModel(myModel); - myDebugSession = session; myOnlyWithDiff = onlyWithDiff; myOnlyWithInstances = onlyWithInstances; myOnlyTracked = onlyTracked; - myInstancesTracker = InstancesTracker.getInstance(myDebugSession.getProject()); + myInstancesTracker = InstancesTracker.getInstance(session.getProject()); myParent = parent; TableColumn classesColumn = getColumnModel().getColumn(DiffViewTableModel.CLASSNAME_COLUMN_INDEX); @@ -115,7 +112,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { public boolean include(Entry entry) { int ix = entry.getIdentifier(); ReferenceType ref = myItems.get(ix); - DiffValue diff = myCounts.getOrDefault(ref, myUnknownValue); + DiffValue diff = myCounts.getOrDefault(ref, UNKNOWN_VALUE); boolean isFilteringOptionsRefused = myOnlyWithDiff && diff.diff() == 0 || myOnlyWithInstances && !diff.hasInstance() @@ -229,7 +226,7 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { ReferenceType ref = classes.get(i); DiffValue oldValue = isInitialized && !myCounts.containsKey(ref) ? new DiffValue(0, 0) - : myCounts.getOrDefault(ref, myUnknownValue); + : myCounts.getOrDefault(ref, UNKNOWN_VALUE); myCounts.put(ref, oldValue.update(counts[i])); } @@ -252,16 +249,12 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { myModel.show(); } - @Nullable @Override public Object getData(@NonNls String dataId) { if (SELECTED_CLASS_KEY.is(dataId)) { return getSelectedClass(); } - if (DEBUG_SESSION_KEY.is(dataId)) { - return myDebugSession; - } if (NEW_INSTANCES_PROVIDER_KEY.is(dataId)) { ReferenceType selectedClass = getSelectedClass(); if (selectedClass != null) { @@ -310,13 +303,13 @@ public class ClassesTable extends JBTable implements DataProvider, Disposable { new AbstractTableColumnDescriptor("Count", Long.class) { @Override public Object getValue(int ix) { - return myCounts.getOrDefault(myItems.get(ix), myUnknownValue).myCurrentCount; + return myCounts.getOrDefault(myItems.get(ix), UNKNOWN_VALUE).myCurrentCount; } }, new AbstractTableColumnDescriptor("Diff", DiffValue.class) { @Override public Object getValue(int ix) { - return myCounts.getOrDefault(myItems.get(ix), myUnknownValue); + return myCounts.getOrDefault(myItems.get(ix), UNKNOWN_VALUE); } } }); diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/ui/InstancesTree.java b/java/debugger/impl/src/com/intellij/debugger/memory/ui/InstancesTree.java index 8bd1c851482c..c6c33e7ce2b6 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/ui/InstancesTree.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/ui/InstancesTree.java @@ -18,10 +18,8 @@ package com.intellij.debugger.memory.ui; import com.intellij.debugger.ui.impl.watch.NodeDescriptorProvider; import com.intellij.debugger.ui.tree.NodeDescriptor; import com.intellij.debugger.ui.tree.ValueDescriptor; -import com.intellij.openapi.actionSystem.DataKey; import com.intellij.openapi.project.Project; import com.intellij.ui.SimpleTextAttributes; -import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider; import com.intellij.xdebugger.frame.*; import com.intellij.xdebugger.impl.actions.XDebuggerActions; @@ -32,7 +30,6 @@ import com.intellij.xdebugger.impl.ui.tree.XDebuggerTreeState; import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; import com.sun.jdi.ObjectReference; import com.sun.jdi.Value; -import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -41,21 +38,17 @@ import java.util.ArrayList; import java.util.List; public class InstancesTree extends XDebuggerTree { - public static final DataKey DEBUG_SESSION_DATA_KEY = DataKey.create("InstancesTree.DebugSession"); private final XValueNodeImpl myRoot; private final Runnable myOnRootExpandAction; - private final XDebugSession myDebugSession; private List myChildren; InstancesTree(@NotNull Project project, - @NotNull XDebugSession debugSession, @NotNull XDebuggerEditorsProvider editorsProvider, @Nullable XValueMarkers valueMarkers, @NotNull Runnable onRootExpand) { super(project, editorsProvider, null, XDebuggerActions.INSPECT_TREE_POPUP_GROUP, valueMarkers); myOnRootExpandAction = onRootExpand; myRoot = new XValueNodeImpl(this, null, "root", new MyRootValue()); - myDebugSession = debugSession; myRoot.children(); setRoot(myRoot, false); @@ -96,16 +89,16 @@ public class InstancesTree extends XDebuggerTree { TreePath selectionPath = getSelectionPath(); Object selectedItem = selectionPath != null ? selectionPath.getLastPathComponent() : null; if (selectedItem instanceof XValueNodeImpl) { - XValueNodeImpl xValueNode = (XValueNodeImpl) selectedItem; + XValueNodeImpl xValueNode = (XValueNodeImpl)selectedItem; XValue valueContainer = xValueNode.getValueContainer(); if (valueContainer instanceof NodeDescriptorProvider) { - NodeDescriptor descriptor = ((NodeDescriptorProvider) valueContainer).getDescriptor(); + NodeDescriptor descriptor = ((NodeDescriptorProvider)valueContainer).getDescriptor(); if (descriptor instanceof ValueDescriptor) { - Value value = ((ValueDescriptor) descriptor).getValue(); + Value value = ((ValueDescriptor)descriptor).getValue(); - if (value instanceof ObjectReference) return (ObjectReference) value; + if (value instanceof ObjectReference) return (ObjectReference)value; } } } @@ -113,16 +106,6 @@ public class InstancesTree extends XDebuggerTree { return null; } - @Nullable - @Override - public Object getData(@NonNls String dataId) { - if (DEBUG_SESSION_DATA_KEY.is(dataId)) { - return myDebugSession; - } - - return super.getData(dataId); - } - enum RebuildPolicy { RELOAD_INSTANCES, ONLY_UPDATE_LABELS } @@ -132,7 +115,8 @@ public class InstancesTree extends XDebuggerTree { public void computeChildren(@NotNull XCompositeNode node) { if (myChildren == null) { myOnRootExpandAction.run(); - } else { + } + else { for (XValueChildrenList children : myChildren) { myRoot.addChildren(children, false); } diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/ui/InstancesWindow.java b/java/debugger/impl/src/com/intellij/debugger/memory/ui/InstancesWindow.java index d9b0c548c413..619eb6fd1dc4 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/ui/InstancesWindow.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/ui/InstancesWindow.java @@ -84,10 +84,10 @@ public class InstancesWindow extends DialogWrapper { private static final int DEFAULT_INSTANCES_LIMIT = 500000; private final Project myProject; - private final XDebugSession myDebugSession; + private final DebugProcessImpl myDebugProcess; private final InstancesProvider myInstancesProvider; private final String myClassName; - private MyInstancesView myInstancesView; + private final MyInstancesView myInstancesView; public InstancesWindow(@NotNull XDebugSession session, @NotNull InstancesProvider provider, @@ -95,19 +95,25 @@ public class InstancesWindow extends DialogWrapper { super(session.getProject(), false); myProject = session.getProject(); - myDebugSession = session; + myDebugProcess = (DebugProcessImpl)DebuggerManager.getInstance(myProject) + .getDebugProcess(session.getDebugProcess().getProcessHandler()); myInstancesProvider = provider; myClassName = className; addWarningMessage(null); - myDebugSession.addSessionListener(new XDebugSessionListener() { + session.addSessionListener(new XDebugSessionListener() { @Override public void sessionStopped() { ApplicationManager.getApplication().invokeLater(() -> close(OK_EXIT_CODE)); } }, myDisposable); setModal(false); + myInstancesView = new MyInstancesView(session); + myInstancesView.setPreferredSize( + new JBDimension(DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT)); + init(); + JRootPane root = myInstancesView.getRootPane(); root.setDefaultButton(myInstancesView.myFilterButton); } @@ -126,9 +132,6 @@ public class InstancesWindow extends DialogWrapper { @Nullable @Override protected JComponent createCenterPanel() { - myInstancesView = new MyInstancesView(); - myInstancesView.setPreferredSize( - new JBDimension(DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT)); return myInstancesView; } @@ -165,33 +168,35 @@ public class InstancesWindow extends DialogWrapper { private final JButton myFilterButton = new JButton("Filter"); private final FilteringProgressView myProgress = new FilteringProgressView(); - private final AnActionListener.Adapter myActionListener = new MyActionListener(); private final Object myFilteringTaskLock = new Object(); private boolean myIsAndroidVM = false; private volatile MyFilteringWorker myFilteringTask = null; - MyInstancesView() { + MyInstancesView(@NotNull XDebugSession session) { super(new BorderLayout(0, JBUI.scale(BORDER_LAYOUT_DEFAULT_GAP))); Disposer.register(InstancesWindow.this.myDisposable, this); - XValueMarkers markers = getValueMarkers(); - ActionManager.getInstance().addAnActionListener(myActionListener, InstancesWindow.this.myDisposable); - myDebugSession.addSessionListener(myDebugSessionListener, InstancesWindow.this.myDisposable); - JavaDebuggerEditorsProvider editorsProvider = new JavaDebuggerEditorsProvider(); + final XValueMarkers markers = getValueMarkers(session); + if (markers != null) { + final MyActionListener listener = new MyActionListener(markers); + ActionManager.getInstance().addAnActionListener(listener, InstancesWindow.this.myDisposable); + } + session.addSessionListener(myDebugSessionListener, InstancesWindow.this.myDisposable); + final JavaDebuggerEditorsProvider editorsProvider = new JavaDebuggerEditorsProvider(); myFilterConditionEditor = new ExpressionEditorWithHistory(myProject, myClassName, editorsProvider, InstancesWindow.this.myDisposable); myFilterButton.setBorder(BorderFactory.createEmptyBorder()); - Dimension filteringButtonSize = myFilterConditionEditor.getEditorComponent().getPreferredSize(); + final Dimension filteringButtonSize = myFilterConditionEditor.getEditorComponent().getPreferredSize(); filteringButtonSize.width = JBUI.scale(FILTERING_BUTTON_ADDITIONAL_WIDTH) + myFilterButton.getPreferredSize().width; myFilterButton.setPreferredSize(filteringButtonSize); - JBPanel filteringPane = new JBPanel(new BorderLayout(JBUI.scale(BORDER_LAYOUT_DEFAULT_GAP), 0)); - JBLabel sideEffectsWarning = new JBLabel("Warning: filtering may have side effects", SwingConstants.RIGHT); + final JBPanel filteringPane = new JBPanel(new BorderLayout(JBUI.scale(BORDER_LAYOUT_DEFAULT_GAP), 0)); + final JBLabel sideEffectsWarning = new JBLabel("Warning: filtering may have side effects", SwingConstants.RIGHT); sideEffectsWarning.setBorder(JBUI.Borders.empty(1, 0, 0, 0)); sideEffectsWarning.setComponentStyle(UIUtil.ComponentStyle.SMALL); sideEffectsWarning.setFontColor(UIUtil.FontColor.BRIGHTER); @@ -203,10 +208,10 @@ public class InstancesWindow extends DialogWrapper { myProgress.addStopActionListener(this::cancelFilteringTask); - myInstancesTree = new InstancesTree(myProject, myDebugSession, editorsProvider, markers, this::updateInstances); + myInstancesTree = new InstancesTree(myProject, editorsProvider, markers, this::updateInstances); myFilterButton.addActionListener(e -> { - String expression = myFilterConditionEditor.getExpression().getExpression(); + final String expression = myFilterConditionEditor.getExpression().getExpression(); if (!expression.isEmpty()) { myFilterConditionEditor.saveTextInHistory(); } @@ -216,7 +221,7 @@ public class InstancesWindow extends DialogWrapper { }); - StackFrameList list = new StackFrameList(myProject, + final StackFrameList list = new StackFrameList(myProject, Collections.emptyList(), GlobalSearchScope.allScope(myProject)); @@ -229,13 +234,13 @@ public class InstancesWindow extends DialogWrapper { } }.installOn(list); - InstancesWithStackFrameView instancesWithStackFrame = new InstancesWithStackFrameView(myDebugSession, + final InstancesWithStackFrameView instancesWithStackFrame = new InstancesWithStackFrameView(session, myInstancesTree, list, myClassName); add(filteringPane, BorderLayout.NORTH); add(instancesWithStackFrame.getComponent(), BorderLayout.CENTER); - JComponent focusedComponent = myFilterConditionEditor.getEditorComponent(); + final JComponent focusedComponent = myFilterConditionEditor.getEditorComponent(); UiNotifyConnector.doWhenFirstShown(focusedComponent, () -> IdeFocusManager.findInstanceByComponent(focusedComponent) .requestFocus(focusedComponent, true)); @@ -248,12 +253,9 @@ public class InstancesWindow extends DialogWrapper { } private void updateInstances() { - DebugProcessImpl debugProcess = (DebugProcessImpl)DebuggerManager.getInstance(myProject) - .getDebugProcess(myDebugSession.getDebugProcess().getProcessHandler()); - cancelFilteringTask(); - debugProcess.getManagerThread().schedule(new DebuggerContextCommandImpl(debugProcess.getDebuggerContext()) { + myDebugProcess.getManagerThread().schedule(new DebuggerContextCommandImpl(myDebugProcess.getDebuggerContext()) { @Override public Priority getPriority() { return Priority.LOWEST; @@ -261,13 +263,13 @@ public class InstancesWindow extends DialogWrapper { @Override public void threadAction(@NotNull SuspendContextImpl suspendContext) { - myIsAndroidVM = AndroidUtil.isAndroidVM(debugProcess.getVirtualMachineProxy().getVirtualMachine()); - int limit = myIsAndroidVM - ? AndroidUtil.ANDROID_INSTANCES_LIMIT - : DEFAULT_INSTANCES_LIMIT; + myIsAndroidVM = AndroidUtil.isAndroidVM(myDebugProcess.getVirtualMachineProxy().getVirtualMachine()); + final int limit = myIsAndroidVM + ? AndroidUtil.ANDROID_INSTANCES_LIMIT + : DEFAULT_INSTANCES_LIMIT; List instances = myInstancesProvider.getInstances(limit + 1); - EvaluationContextImpl evaluationContext = debugProcess + final EvaluationContextImpl evaluationContext = myDebugProcess .getDebuggerContext().createEvaluationContext(); if (instances.size() > limit) { @@ -296,9 +298,9 @@ public class InstancesWindow extends DialogWrapper { } } - private XValueMarkers getValueMarkers() { - return myDebugSession instanceof XDebugSessionImpl - ? ((XDebugSessionImpl)myDebugSession).getValueMarkers() + private XValueMarkers getValueMarkers(@NotNull XDebugSession session) { + return session instanceof XDebugSessionImpl + ? ((XDebugSessionImpl)session).getValueMarkers() : null; } @@ -326,15 +328,19 @@ public class InstancesWindow extends DialogWrapper { } private class MyActionListener extends AnActionListener.Adapter { + private final XValueMarkers myValueMarkers; + + private MyActionListener(@NotNull XValueMarkers markers) { + myValueMarkers = markers; + } + @Override public void beforeActionPerformed(AnAction action, DataContext dataContext, AnActionEvent event) { - if (dataContext.getData(PlatformDataKeys.CONTEXT_COMPONENT) == myInstancesView.myInstancesTree && + if (dataContext.getData(PlatformDataKeys.CONTEXT_COMPONENT) == myInstancesTree && (isAddToWatchesAction(action) || isEvaluateExpressionAction(action))) { - final XValueNodeImpl selectedNode = XDebuggerTreeActionBase.getSelectedNode(dataContext); - final XValueMarkers markers = getValueMarkers(); - - if (markers != null && selectedNode != null) { + XValueNodeImpl selectedNode = XDebuggerTreeActionBase.getSelectedNode(dataContext); + if (selectedNode != null) { TreeNode currentNode = selectedNode; while (!myInstancesTree.getRoot().equals(currentNode.getParent())) { currentNode = currentNode.getParent(); @@ -344,8 +350,8 @@ public class InstancesWindow extends DialogWrapper { final String expression = valueContainer.getEvaluationExpression(); if (expression != null) { - markers.markValue(valueContainer, - new ValueMarkup(expression.replace("@", ""), new JBColor(0, 0), null)); + myValueMarkers.markValue(valueContainer, + new ValueMarkup(expression.replace("@", ""), new JBColor(0, 0), null)); } ApplicationManager.getApplication().invokeLater(() -> myInstancesTree @@ -481,9 +487,7 @@ public class InstancesWindow extends DialogWrapper { @NotNull XExpression expression, @NotNull EvaluationContextImpl evaluationContext) { if (refs.size() != 0) { - DebugProcessImpl debugProcess = - (DebugProcessImpl)DebuggerManager.getInstance(myProject).getDebugProcess(myDebugSession.getDebugProcess().getProcessHandler()); - myTask = new FilteringTask(refs.get(0).referenceType(), debugProcess, expression, refs, + myTask = new FilteringTask(refs.get(0).referenceType(), myDebugProcess, expression, refs, new MyFilteringCallback(evaluationContext)); } else { diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/ui/InstancesWithStackFrameView.java b/java/debugger/impl/src/com/intellij/debugger/memory/ui/InstancesWithStackFrameView.java index 234d6330f7a1..23d678182978 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/ui/InstancesWithStackFrameView.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/ui/InstancesWithStackFrameView.java @@ -15,6 +15,12 @@ */ package com.intellij.debugger.memory.ui; +import com.intellij.debugger.DebuggerManager; +import com.intellij.debugger.memory.component.InstancesTracker; +import com.intellij.debugger.memory.component.MemoryViewDebugProcessData; +import com.intellij.debugger.memory.event.InstancesTrackerListener; +import com.intellij.debugger.memory.tracking.TrackingType; +import com.intellij.debugger.memory.utils.StackFrameItem; import com.intellij.icons.AllIcons; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; @@ -25,11 +31,6 @@ import com.intellij.ui.components.labels.ActionLink; import com.intellij.xdebugger.XDebugSession; import com.sun.jdi.ObjectReference; import org.jetbrains.annotations.NotNull; -import com.intellij.debugger.memory.component.CreationPositionTracker; -import com.intellij.debugger.memory.component.InstancesTracker; -import com.intellij.debugger.memory.event.InstancesTrackerListener; -import com.intellij.debugger.memory.tracking.TrackingType; -import com.intellij.debugger.memory.utils.StackFrameItem; import javax.swing.*; import java.util.Collections; @@ -77,7 +78,6 @@ class InstancesWithStackFrameView { JComponent stackComponent = new JBScrollPane(list); - CreationPositionTracker tracker = CreationPositionTracker.getInstance(debugSession.getProject()); InstancesTracker instancesTracker = InstancesTracker.getInstance(debugSession.getProject()); instancesTracker.addTrackerListener(new InstancesTrackerListener() { @Override @@ -100,10 +100,13 @@ class InstancesWithStackFrameView { mySplitter.setHonorComponentsMinimumSize(false); myHidedProportion = DEFAULT_SPLITTER_PROPORTION; + final MemoryViewDebugProcessData data = + DebuggerManager.getInstance(debugSession.getProject()).getDebugProcess(debugSession.getDebugProcess().getProcessHandler()) + .getUserData(MemoryViewDebugProcessData.KEY); tree.addTreeSelectionListener(e -> { ObjectReference ref = tree.getSelectedReference(); - if (ref != null && tracker != null) { - List stack = tracker.getStack(debugSession, ref); + if (ref != null && data != null) { + List stack = data.getTrackedStacks().getStack(ref); if (stack != null) { list.setFrame(stack); if (mySplitter.getProportion() == 1.f) { diff --git a/resources/src/componentSets/Debugger.xml b/resources/src/componentSets/Debugger.xml index 2a8751ee98e0..deea1b445d1d 100644 --- a/resources/src/componentSets/Debugger.xml +++ b/resources/src/componentSets/Debugger.xml @@ -29,10 +29,6 @@ com.intellij.debugger.memory.component.InstancesTracker - - - com.intellij.debugger.memory.component.CreationPositionTracker -