mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge branch 'fix-memory-leak'
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+10
-11
@@ -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);
|
||||
|
||||
+10
-4
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+16
-11
@@ -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();
|
||||
}
|
||||
|
||||
+22
-17
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+10
-14
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
-117
@@ -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;
|
||||
}
|
||||
}
|
||||
+95
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+40
@@ -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();
|
||||
}
|
||||
+66
-60
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+9
-16
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+44
-31
@@ -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 {
|
||||
|
||||
+11
-8
@@ -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) {
|
||||
|
||||
@@ -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">
|
||||
|
||||
Reference in New Issue
Block a user