Merge branch 'fix-memory-leak'

This commit is contained in:
Vitaliy.Bibaev
2017-01-11 17:03:38 +03:00
18 changed files with 550 additions and 495 deletions
@@ -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);
}
}
@@ -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<ReferenceType> referenceTypes = vm.classesByName(elementTypeName);
if (referenceTypes.size() == 1) {
return referenceTypes.get(0);
@@ -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();
}
}
}
@@ -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();
}
@@ -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<StackFrameItem> 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<StackFrameItem> 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<StackFrameItem> 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;
}
}
@@ -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);
}
}
@@ -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<XDebugSession, Map<ObjectReference, List<StackFrameItem>>>
mySession2Reference2Stack = new ConcurrentHashMap<>();
private final ConcurrentHashMap<XDebugSession, Map<ObjectReference, List<StackFrameItem>>>
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<StackFrameItem> getStack(@NotNull XDebugSession session, @NotNull ObjectReference ref) {
List<StackFrameItem> stack = extract(mySession2Reference2Stack, session, ref);
return stack != null ? stack : extract(myPinnedSession2Reference2Stack, session, ref);
}
public void addStack(@NotNull XDebugSession session, @NotNull ObjectReference ref,
@NotNull List<StackFrameItem> 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<ObjectReference, List<StackFrameItem>> ref2Stack = myPinnedSession2Reference2Stack.getOrDefault(session, null);
if (ref2Stack != null) {
Iterator<ObjectReference> 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<ObjectReference, List<StackFrameItem>> ref2Stack = mySession2Reference2Stack.getOrDefault(session, null);
if (ref2Stack != null) {
Map<ObjectReference, List<StackFrameItem>> 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> T extract(@NotNull Map<XDebugSession, Map<ObjectReference, T>> map,
@NotNull XDebugSession session, @NotNull ObjectReference ref) {
Map<ObjectReference, T> ref2something = map.getOrDefault(session, null);
if(ref2something != null) {
return ref2something.getOrDefault(ref, null);
}
return null;
}
}
@@ -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<MemoryViewDebugProcessData> 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<ReferenceType, Map<ObjectReference, List<StackFrameItem>>> myType2Reference2Stack = new ConcurrentHashMap<>();
private final Map<ReferenceType, Map<ObjectReference, List<StackFrameItem>>> myPinnedType2Reference2Stack = new ConcurrentHashMap<>();
@Nullable
@Override
public List<StackFrameItem> getStack(@NotNull ObjectReference reference) {
final List<StackFrameItem> stack = extract(myType2Reference2Stack, reference);
return stack != null ? stack : extract(myPinnedType2Reference2Stack, reference);
}
@Override
public void addStack(@NotNull ObjectReference ref, @NotNull List<StackFrameItem> frames) {
myType2Reference2Stack.computeIfAbsent(ref.referenceType(), referenceType -> new ConcurrentHashMap<>()).put(ref, frames);
}
@Override
public void pinStacks(@NotNull ReferenceType referenceType) {
final Map<ObjectReference, List<StackFrameItem>> 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<StackFrameItem> extract(@NotNull Map<ReferenceType, Map<ObjectReference, List<StackFrameItem>>> map,
@NotNull ObjectReference ref) {
final Map<ObjectReference, List<StackFrameItem>> ref2Stack = map.get(ref.referenceType());
return ref2Stack != null ? ref2Stack.get(ref) : null;
}
}
}
@@ -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<StackFrameItem> getStack(@NotNull ObjectReference reference);
void addStack(@NotNull ObjectReference ref, @NotNull List<StackFrameItem> frames);
void pinStacks(@NotNull ReferenceType referenceType);
void unpinStacks(@NotNull ReferenceType referenceType);
void release();
}
@@ -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<XDebugSession, ClassesFilteredView> myMemoryViews = new ConcurrentHashMap<>();
private final EventDispatcher<ToolWindowStateListener> 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<Project> {
@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);
}
}
@@ -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);
}
}
}
}
@@ -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<JavaLineBreakpointProperties> implements Disposable {
private boolean myIsEnabled = false;
private final List<BreakpointRequest> 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<StackFrameItem> frame = StackFrameItem.createFrames(suspendContext.getThread());
data.getTrackedStacks().addStack(thisRef, frame);
}
}
}
@@ -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<ReferenceType, ConstructorInstancesTracker> 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
* <p>
* 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<String, TrackingType> entry : myInstancesTracker.getTrackedClasses().entrySet()) {
TrackingType type = entry.getValue();
String className = entry.getKey();
List<ReferenceType> classes = myDebugProcess.getVirtualMachineProxy().classesByName(className);
List<ReferenceType> 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<ReferenceType> classes = myDebugProcess.getVirtualMachineProxy().allClasses();
final List<ReferenceType> 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<long[]> chunks = new SmartList<>();
int size = classes.size();
final List<long[]> 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<ReferenceType> batch = classes.subList(begin, end);
final List<ReferenceType> 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<ObjectReference> 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<ObjectReference> 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();
}
}
}
}
@@ -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<ReferenceType> SELECTED_CLASS_KEY = DataKey.create("ClassesTable.SelectedClass");
public static final DataKey<XDebugSession> DEBUG_SESSION_KEY = DataKey.create("ClassesTable.DebugSession");
public static final DataKey<InstancesProvider> NEW_INSTANCES_PROVIDER_KEY =
DataKey.create("ClassesTable.NewInstances");
public static final DataKey<ReferenceCountProvider> 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<ReferenceType, DiffValue> 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<? extends DiffViewTableModel, ? extends Integer> 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);
}
}
});
@@ -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<XDebugSession> DEBUG_SESSION_DATA_KEY = DataKey.create("InstancesTree.DebugSession");
private final XValueNodeImpl myRoot;
private final Runnable myOnRootExpandAction;
private final XDebugSession myDebugSession;
private List<XValueChildrenList> 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);
}
@@ -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<ObjectReference> 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 {
@@ -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<StackFrameItem> stack = tracker.getStack(debugSession, ref);
if (ref != null && data != null) {
List<StackFrameItem> stack = data.getTrackedStacks().getStack(ref);
if (stack != null) {
list.setFrame(stack);
if (mySplitter.getProportion() == 1.f) {
-4
View File
@@ -29,10 +29,6 @@
<component>
<implementation-class>com.intellij.debugger.memory.component.InstancesTracker</implementation-class>
</component>
<component>
<implementation-class>com.intellij.debugger.memory.component.CreationPositionTracker</implementation-class>
</component>
</project-components>
<extensions defaultExtensionNs="com.intellij">