mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-18 09:34:34 +07:00
Initial: memory agent
This commit is contained in:
@@ -21,8 +21,8 @@
|
||||
<orderEntry type="module" module-name="intellij.platform.ide.impl" />
|
||||
<orderEntry type="module" module-name="intellij.platform.util" />
|
||||
<orderEntry type="module" module-name="intellij.platform.jps.build" />
|
||||
<orderEntry type="library" name="sa-jdwp" level="project" />
|
||||
<orderEntry type="module" module-name="intellij.java.debugger.memory.agent" />
|
||||
<orderEntry type="library" name="sa-jdwp" level="project" />
|
||||
</component>
|
||||
<component name="copyright">
|
||||
<Base>
|
||||
|
||||
+24
-4
@@ -8,6 +8,7 @@ import com.intellij.debugger.engine.SuspendContextImpl;
|
||||
import com.intellij.debugger.engine.evaluation.EvaluateException;
|
||||
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
|
||||
import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
|
||||
import com.intellij.debugger.engine.ReferringObjectsProvider;
|
||||
import com.intellij.debugger.ui.impl.watch.FieldDescriptorImpl;
|
||||
import com.intellij.debugger.ui.impl.watch.NodeManagerImpl;
|
||||
import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl;
|
||||
@@ -27,22 +28,39 @@ import java.util.List;
|
||||
|
||||
public class JavaReferringObjectsValue extends JavaValue {
|
||||
private static final long MAX_REFERRING = 100;
|
||||
private final ReferringObjectsProvider myReferringObjectsProvider;
|
||||
private final boolean myIsField;
|
||||
|
||||
private JavaReferringObjectsValue(@Nullable JavaValue parent,
|
||||
@NotNull ValueDescriptorImpl valueDescriptor,
|
||||
@NotNull EvaluationContextImpl evaluationContext,
|
||||
@NotNull ReferringObjectsProvider referringObjectsProvider,
|
||||
NodeManagerImpl nodeManager,
|
||||
boolean isField) {
|
||||
super(parent, valueDescriptor, evaluationContext, nodeManager, false);
|
||||
myReferringObjectsProvider = referringObjectsProvider;
|
||||
myIsField = isField;
|
||||
}
|
||||
|
||||
public JavaReferringObjectsValue(@NotNull JavaValue javaValue, boolean isField) {
|
||||
public JavaReferringObjectsValue(@NotNull JavaValue javaValue,
|
||||
@NotNull ReferringObjectsProvider referringObjectsProvider,
|
||||
boolean isField) {
|
||||
super(null, javaValue.getName(), javaValue.getDescriptor(), javaValue.getEvaluationContext(), javaValue.getNodeManager(), false);
|
||||
myReferringObjectsProvider = referringObjectsProvider;
|
||||
myIsField = isField;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public XReferrersProvider getReferrersProvider() {
|
||||
return new XReferrersProvider() {
|
||||
@Override
|
||||
public XValue getReferringObjectsValue() {
|
||||
return new JavaReferringObjectsValue(JavaReferringObjectsValue.this, myReferringObjectsProvider, false);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void computeChildren(@NotNull final XCompositeNode node) {
|
||||
scheduleCommand(getEvaluationContext(), node, new SuspendContextCommandImpl(getEvaluationContext().getSuspendContext()) {
|
||||
@@ -59,7 +77,7 @@ public class JavaReferringObjectsValue extends JavaValue {
|
||||
|
||||
List<ObjectReference> references;
|
||||
try {
|
||||
references = ((ObjectReference)value).referringObjects(MAX_REFERRING);
|
||||
references = myReferringObjectsProvider.getReferringObjects((ObjectReference)value, MAX_REFERRING);
|
||||
} catch (ObjectCollectedException e) {
|
||||
node.setErrorMessage(DebuggerBundle.message("evaluation.error.object.collected"));
|
||||
return;
|
||||
@@ -76,7 +94,8 @@ public class JavaReferringObjectsValue extends JavaValue {
|
||||
return reference;
|
||||
}
|
||||
};
|
||||
children.add(new JavaReferringObjectsValue(null, descriptor, getEvaluationContext(), getNodeManager(), true));
|
||||
children.add(new JavaReferringObjectsValue(null, descriptor, getEvaluationContext(),
|
||||
myReferringObjectsProvider, getNodeManager(), true));
|
||||
i++;
|
||||
}
|
||||
else {
|
||||
@@ -96,7 +115,8 @@ public class JavaReferringObjectsValue extends JavaValue {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
children.add("Referrer " + i++, new JavaReferringObjectsValue(null, descriptor, getEvaluationContext(), getNodeManager(), false));
|
||||
children.add("Referrer " + i++, new JavaReferringObjectsValue(null, descriptor, getEvaluationContext(),
|
||||
myReferringObjectsProvider, getNodeManager(), false));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -650,7 +650,7 @@ public class JavaValue extends XNamedValue implements NodeDescriptorProvider, XV
|
||||
return new XReferrersProvider() {
|
||||
@Override
|
||||
public XValue getReferringObjectsValue() {
|
||||
return new JavaReferringObjectsValue(JavaValue.this, false);
|
||||
return new JavaReferringObjectsValue(JavaValue.this, ReferringObjectsProvider.BASIC_JDI, false);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.debugger.engine;
|
||||
|
||||
import com.sun.jdi.ObjectReference;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface ReferringObjectsProvider {
|
||||
@NotNull
|
||||
List<ObjectReference> getReferringObjects(@NotNull ObjectReference value, long limit);
|
||||
|
||||
ReferringObjectsProvider BASIC_JDI = new ReferringObjectsProvider() {
|
||||
@NotNull
|
||||
@Override
|
||||
public List<ObjectReference> getReferringObjects(@NotNull ObjectReference value, long limit) {
|
||||
return value.referringObjects(limit);
|
||||
}
|
||||
};
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.debugger.memory.action;
|
||||
|
||||
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
|
||||
import com.intellij.debugger.memory.agent.AgentLoader;
|
||||
import com.intellij.debugger.memory.agent.MemoryAgent;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.ui.messages.MessageDialog;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl;
|
||||
import com.sun.jdi.ObjectReference;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author Vitaliy.Bibaev
|
||||
*/
|
||||
public class CalculateRetainedSizeAction extends NativeAgentActionBase {
|
||||
@Override
|
||||
protected void perform(@NotNull EvaluationContextImpl evaluationContext,
|
||||
@NotNull ObjectReference reference,
|
||||
@NotNull XValueNodeImpl node) {
|
||||
MemoryAgent memoryAgent = new AgentLoader().load(evaluationContext, evaluationContext.getDebugProcess().getVirtualMachineProxy());
|
||||
long size = memoryAgent.evaluateObjectSize(reference);
|
||||
ApplicationManager.getApplication().invokeLater(
|
||||
() -> new MessageDialog(node.getTree().getProject(), String.valueOf(size), "Size of the Object",
|
||||
ArrayUtil.EMPTY_STRING_ARRAY, 0, null, false)
|
||||
.show());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.debugger.memory.action;
|
||||
|
||||
import com.intellij.debugger.engine.DebugProcessImpl;
|
||||
import com.intellij.debugger.engine.JavaValue;
|
||||
import com.intellij.debugger.engine.SuspendContextImpl;
|
||||
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
|
||||
import com.intellij.debugger.engine.managerThread.DebuggerCommand;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.xdebugger.XDebugSession;
|
||||
import com.intellij.xdebugger.XDebuggerManager;
|
||||
import com.intellij.xdebugger.frame.XSuspendContext;
|
||||
import com.intellij.xdebugger.frame.XValue;
|
||||
import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl;
|
||||
import com.sun.jdi.ObjectReference;
|
||||
import com.sun.jdi.Value;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public abstract class NativeAgentActionBase extends DebuggerTreeAction {
|
||||
protected final Logger LOG = Logger.getInstance(this.getClass());
|
||||
|
||||
@Override
|
||||
protected void perform(XValueNodeImpl node, @NotNull String nodeName, AnActionEvent e) {
|
||||
Project project = node.getTree().getProject();
|
||||
XValue container = node.getValueContainer();
|
||||
XDebugSession currentSession = XDebuggerManager.getInstance(project).getCurrentSession();
|
||||
XSuspendContext suspendContext = currentSession != null ? currentSession.getSuspendContext() : null;
|
||||
DebugProcessImpl debugProcess =
|
||||
suspendContext instanceof SuspendContextImpl ? ((SuspendContextImpl)suspendContext).getDebugProcess() : null;
|
||||
if (debugProcess == null) return;
|
||||
debugProcess.getManagerThread().invokeCommand(new DebuggerCommand() {
|
||||
@Override
|
||||
public void action() {
|
||||
if (container instanceof JavaValue) {
|
||||
EvaluationContextImpl evaluationContext = debugProcess.getDebuggerContext().createEvaluationContext();
|
||||
if (evaluationContext == null) return;
|
||||
Value value = ((JavaValue)container).getDescriptor().getValue();
|
||||
perform(evaluationContext, (ObjectReference)value, node);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commandCancelled() {
|
||||
LOG.info("command cancelled");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isEnabled(@NotNull XValueNodeImpl node, @NotNull AnActionEvent e) {
|
||||
if (!super.isEnabled(node, e)) return false;
|
||||
XValue container = node.getValueContainer();
|
||||
if (container instanceof JavaValue) {
|
||||
if (((JavaValue)container).getDescriptor().getValue() instanceof ObjectReference) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected abstract void perform(@NotNull EvaluationContextImpl evaluationContext,
|
||||
@NotNull ObjectReference reference,
|
||||
@NotNull XValueNodeImpl node);
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.debugger.memory.action;
|
||||
|
||||
import com.intellij.debugger.actions.JavaReferringObjectsValue;
|
||||
import com.intellij.debugger.engine.JavaValue;
|
||||
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
|
||||
import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
|
||||
import com.intellij.debugger.memory.agent.AgentLoader;
|
||||
import com.intellij.debugger.memory.agent.MemoryAgent;
|
||||
import com.intellij.debugger.engine.ReferringObjectsProvider;
|
||||
import com.intellij.notification.NotificationType;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.xdebugger.impl.XDebuggerManagerImpl;
|
||||
import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree;
|
||||
import com.intellij.xdebugger.impl.ui.tree.XInspectDialog;
|
||||
import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl;
|
||||
import com.sun.jdi.ObjectReference;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ShowGarbageCollectorRootsAction extends NativeAgentActionBase {
|
||||
@Override
|
||||
protected void perform(@NotNull EvaluationContextImpl evaluationContext,
|
||||
@NotNull ObjectReference reference,
|
||||
@NotNull XValueNodeImpl node) {
|
||||
VirtualMachineProxyImpl virtualMachineProxy = evaluationContext.getDebugProcess().getVirtualMachineProxy();
|
||||
MemoryAgent memoryAgent = new AgentLoader().load(evaluationContext, virtualMachineProxy);
|
||||
ReferringObjectsProvider roots = memoryAgent.canFindGcRoots() ? memoryAgent.findGcRoots(reference) : null;
|
||||
if (roots == null) {
|
||||
XDebuggerManagerImpl.NOTIFICATION_GROUP.createNotification("This feature is unavailable", NotificationType.INFORMATION);
|
||||
return;
|
||||
}
|
||||
ApplicationManager.getApplication().invokeLater(
|
||||
() -> {
|
||||
XDebuggerTree tree = node.getTree();
|
||||
JavaValue javaValue = (JavaValue)node.getValueContainer();
|
||||
JavaReferringObjectsValue value = new JavaReferringObjectsValue(javaValue, roots, false);
|
||||
XInspectDialog dialog =
|
||||
new XInspectDialog(tree.getProject(), tree.getEditorsProvider(), tree.getSourcePosition(), StringUtil.notNullize(node.getName()),
|
||||
value, tree.getValueMarkers(),
|
||||
evaluationContext.getDebugProcess().getSession().getXDebugSession(), false);
|
||||
dialog.setTitle("Paths to GC Roots");
|
||||
dialog.show();
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.debugger.memory.agent;
|
||||
|
||||
import com.intellij.debugger.engine.DebugProcessImpl;
|
||||
import com.intellij.debugger.engine.DebuggerManagerThreadImpl;
|
||||
import com.intellij.debugger.engine.ReferringObjectsProvider;
|
||||
import com.intellij.debugger.engine.evaluation.EvaluateException;
|
||||
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
|
||||
import com.intellij.debugger.engine.jdi.VirtualMachineProxy;
|
||||
import com.intellij.debugger.impl.ClassLoadingUtils;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.sun.jdi.ClassLoaderReference;
|
||||
import com.sun.jdi.ClassType;
|
||||
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 class AgentLoader {
|
||||
private static final Logger LOG = Logger.getInstance(AgentLoader.class);
|
||||
private static final MemoryAgent DEFAULT_PROXY = new MyDisabledMemoryAgent();
|
||||
|
||||
@NotNull
|
||||
public MemoryAgent load(@NotNull EvaluationContextImpl evaluationContext, @NotNull VirtualMachineProxy virtualMachine) {
|
||||
DebuggerManagerThreadImpl.assertIsManagerThread();
|
||||
try {
|
||||
ClassType classType = ensureClassLoaded(evaluationContext, virtualMachine);
|
||||
return classType == null ? DEFAULT_PROXY : new MemoryAgentImpl(evaluationContext, classType);
|
||||
}
|
||||
catch (EvaluateException e) {
|
||||
LOG.error("Could not load proxy class", e);
|
||||
return DEFAULT_PROXY;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static ClassType ensureClassLoaded(@NotNull EvaluationContextImpl context, @NotNull VirtualMachineProxy vm)
|
||||
throws EvaluateException {
|
||||
List<ReferenceType> classes = vm.classesByName(MemoryAgentImpl.PROXY_CLASS_NAME);
|
||||
if (classes.isEmpty()) {
|
||||
ClassType classType = loadUtilityClass(context);
|
||||
if (classType == null) {
|
||||
LOG.error("Could not load proxy class");
|
||||
}
|
||||
return classType;
|
||||
}
|
||||
|
||||
LOG.assertTrue(classes.size() == 1, "Too many utility classes loaded: " + classes.size());
|
||||
return (ClassType)classes.get(0);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static ClassType loadUtilityClass(@NotNull EvaluationContextImpl context) throws EvaluateException {
|
||||
DebugProcessImpl debugProcess = context.getDebugProcess();
|
||||
byte[] bytes = readUtilityClass();
|
||||
ClassLoaderReference classLoader = ClassLoadingUtils.getClassLoader(context, debugProcess);
|
||||
ClassLoadingUtils.defineClass(MemoryAgentImpl.PROXY_CLASS_NAME, bytes, context, debugProcess, classLoader);
|
||||
return (ClassType)debugProcess.findClass(context, MemoryAgentImpl.PROXY_CLASS_NAME, classLoader);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static byte[] readUtilityClass() {
|
||||
return new ProxyExtractor().extractProxy();
|
||||
}
|
||||
|
||||
private static class MyDisabledMemoryAgent implements MemoryAgent {
|
||||
@Override
|
||||
public boolean canEvaluateObjectSize() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long evaluateObjectSize(@NotNull ObjectReference reference) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canEvaluateObjectsSizes() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> evaluateObjectsSizes(@NotNull List<ObjectReference> references) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canFindGcRoots() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReferringObjectsProvider findGcRoots(@NotNull ObjectReference reference) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.debugger.memory.agent;
|
||||
|
||||
import com.intellij.debugger.engine.ReferringObjectsProvider;
|
||||
import com.sun.jdi.ObjectReference;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface MemoryAgent {
|
||||
boolean canEvaluateObjectSize();
|
||||
|
||||
long evaluateObjectSize(@NotNull ObjectReference reference);
|
||||
|
||||
boolean canEvaluateObjectsSizes();
|
||||
|
||||
List<Long> evaluateObjectsSizes(@NotNull List<ObjectReference> references);
|
||||
|
||||
boolean canFindGcRoots();
|
||||
|
||||
@Nullable
|
||||
ReferringObjectsProvider findGcRoots(@NotNull ObjectReference reference);
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.debugger.memory.agent;
|
||||
|
||||
import com.intellij.debugger.engine.ReferringObjectsProvider;
|
||||
import com.intellij.debugger.engine.evaluation.EvaluateException;
|
||||
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
|
||||
import com.intellij.debugger.memory.agent.parsers.GcRootsPathsParser;
|
||||
import com.intellij.debugger.memory.agent.parsers.LongValueParser;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.sun.jdi.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class MemoryAgentImpl implements MemoryAgent {
|
||||
public static final String PROXY_CLASS_NAME = "com.intellij.memory.agent.proxy.IdeaNativeAgentProxy";
|
||||
|
||||
private static final Logger LOG = Logger.getInstance(MemoryAgentImpl.class);
|
||||
|
||||
private static final String SIZE_OF_SINGLE_OBJECT_METHOD_NAME = "size";
|
||||
private static final String SIZE_OF_OBJECTS_METHOD_NAME = "size";
|
||||
private static final String GARBAGE_COLLECTOR_ROOTS_METHOD_NAME = "gcRoots";
|
||||
|
||||
private final EvaluationContextImpl myEvaluationContext;
|
||||
private final ClassType myProxyClassType;
|
||||
|
||||
public MemoryAgentImpl(@NotNull EvaluationContextImpl context, @NotNull ClassType reference) {
|
||||
myEvaluationContext = context;
|
||||
myProxyClassType = reference;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canEvaluateObjectSize() {
|
||||
return !myProxyClassType.methodsByName(SIZE_OF_SINGLE_OBJECT_METHOD_NAME).isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long evaluateObjectSize(@NotNull ObjectReference reference) {
|
||||
if (!canEvaluateObjectSize()) throw new UnsupportedOperationException();
|
||||
Value result = callMethod(SIZE_OF_SINGLE_OBJECT_METHOD_NAME, Collections.singletonList(reference));
|
||||
return result != null ? new LongValueParser().parse(result) : -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canEvaluateObjectsSizes() {
|
||||
return !myProxyClassType.methodsByName(SIZE_OF_OBJECTS_METHOD_NAME).isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> evaluateObjectsSizes(@NotNull List<ObjectReference> references) {
|
||||
if (!canEvaluateObjectsSizes()) throw new UnsupportedOperationException();
|
||||
Value result = callMethod(SIZE_OF_OBJECTS_METHOD_NAME, references);
|
||||
// TODO: Implement method and conversion
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canFindGcRoots() {
|
||||
return !myProxyClassType.methodsByName(GARBAGE_COLLECTOR_ROOTS_METHOD_NAME).isEmpty();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ReferringObjectsProvider findGcRoots(@NotNull ObjectReference reference) {
|
||||
if (!canFindGcRoots()) throw new UnsupportedOperationException();
|
||||
Value value = callMethod(GARBAGE_COLLECTOR_ROOTS_METHOD_NAME, Collections.singletonList(reference));
|
||||
return value == null ? null : new GcRootsPathsParser().parse(value);
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
private Value callMethod(@NotNull String methodName,
|
||||
@NotNull List<? extends Value> args) {
|
||||
List<Method> methods = myProxyClassType.methodsByName(methodName);
|
||||
if (methods.isEmpty()) {
|
||||
LOG.error("Method \"" + methodName + "\" not found");
|
||||
return null;
|
||||
}
|
||||
if (methods.size() > 1) {
|
||||
LOG.warn("Too many methods \"" + methodName + "\" found. Count: " + methods.size());
|
||||
}
|
||||
|
||||
Method method = methods.get(0);
|
||||
if (!method.isStatic()) {
|
||||
LOG.error("Utility method should be static");
|
||||
}
|
||||
try {
|
||||
return myEvaluationContext.getDebugProcess().invokeMethod(myEvaluationContext, myProxyClassType, method, args);
|
||||
}
|
||||
catch (EvaluateException e) {
|
||||
LOG.error("Something went wrong", e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static long parseLong(@Nullable Value value) {
|
||||
if (value instanceof PrimitiveValue) {
|
||||
return ((PrimitiveValue)value).longValue();
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Unexpected argument. Primitive value is expected");
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.debugger.memory.agent;
|
||||
|
||||
import com.intellij.debugger.engine.ReferringObjectsProvider;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.sun.jdi.ObjectReference;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class MemoryAgentReferringObjectProvider implements ReferringObjectsProvider {
|
||||
private static final Logger LOG = Logger.getInstance(MemoryAgentReferringObjectProvider.class);
|
||||
|
||||
private final Map<ObjectReference, Integer> myReversedMap = new HashMap<>();
|
||||
private final List<ObjectReference> myDirectMap;
|
||||
private final List<List<Integer>> myPaths;
|
||||
|
||||
public MemoryAgentReferringObjectProvider(@NotNull List<ObjectReference> values, @NotNull List<List<Integer>> paths) {
|
||||
myDirectMap = values;
|
||||
for (int i = 0; i < values.size(); i++) {
|
||||
myReversedMap.put(values.get(i), i);
|
||||
}
|
||||
|
||||
myPaths = paths;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<ObjectReference> getReferringObjects(@NotNull ObjectReference value, long limit) {
|
||||
Integer index = myReversedMap.get(value);
|
||||
if (index == null) {
|
||||
LOG.error("Could not find referring object for reference " + value.toString());
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return myPaths.get(index).stream().limit(limit).map(ix -> myDirectMap.get(ix)).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.debugger.memory.agent.parsers
|
||||
|
||||
import com.intellij.debugger.memory.agent.MemoryAgentReferringObjectProvider
|
||||
import com.intellij.debugger.engine.ReferringObjectsProvider
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.sun.jdi.ArrayReference
|
||||
import com.sun.jdi.IntegerValue
|
||||
import com.sun.jdi.ObjectReference
|
||||
import com.sun.jdi.Value
|
||||
import java.util.ArrayList
|
||||
|
||||
class GcRootsPathsParser : ResultParser<ReferringObjectsProvider> {
|
||||
private companion object {
|
||||
val LOG = Logger.getInstance(GcRootsPathsParser::class.java)
|
||||
}
|
||||
|
||||
override fun parse(value: Value): ReferringObjectsProvider {
|
||||
if (value is ArrayReference) {
|
||||
LOG.assertTrue(value.length() == 2, "Array must represent 2 values: objects and backward references")
|
||||
val values = parseValues(value.getValue(0))
|
||||
val backwardReferences = parseBackwardReferences(value.getValue(1))
|
||||
return MemoryAgentReferringObjectProvider(values, backwardReferences)
|
||||
}
|
||||
throw AssertionError("Incorrect result format: array of arrays is expected")
|
||||
}
|
||||
|
||||
private fun parseBackwardReferences(value: Value): List<List<Int>> {
|
||||
if (value is ArrayReference) {
|
||||
val result = ArrayList<List<Int>>()
|
||||
for (item in value.values) {
|
||||
if (item !is ArrayReference || "int" == item.type().name()) {
|
||||
throw AssertionError("Incorrect result format: int array expected")
|
||||
}
|
||||
val ints = item.values.map { x -> (x as IntegerValue).value() }
|
||||
result.add(ints)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
throw AssertionError("Incorrect result format: array with nested int arrays expected")
|
||||
}
|
||||
|
||||
private fun parseValues(value: Value): List<ObjectReference> {
|
||||
if (value is ArrayReference) {
|
||||
val result = ArrayList<ObjectReference>()
|
||||
for (item in value.values) {
|
||||
if (item !is ObjectReference) break
|
||||
result.add(item)
|
||||
}
|
||||
|
||||
if (result.size != value.length()) {
|
||||
throw AssertionError("All values should be object references but some of them are not")
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
throw AssertionError("Incorrect result format: array with object references expected")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.debugger.memory.agent.parsers
|
||||
|
||||
import com.sun.jdi.PrimitiveValue
|
||||
import com.sun.jdi.Value
|
||||
|
||||
class LongValueParser : ResultParser<Long> {
|
||||
override fun parse(value: Value): Long {
|
||||
if (value is PrimitiveValue) {
|
||||
return value.longValue()
|
||||
}
|
||||
|
||||
throw IllegalArgumentException("Unexpected argument. Primitive value is expected")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.debugger.memory.agent.parsers;
|
||||
|
||||
import com.sun.jdi.Value;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public interface ResultParser<T> {
|
||||
T parse(@NotNull Value value);
|
||||
}
|
||||
@@ -4,13 +4,12 @@
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/resources" type="java-resource" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||
<orderEntry type="module" module-name="intellij.platform.util" />
|
||||
<orderEntry type="library" name="debugger-memory-agent" level="project" />
|
||||
<orderEntry type="library" name="commons-io" level="project" />
|
||||
<orderEntry type="module" module-name="intellij.platform.util" />
|
||||
<orderEntry type="library" name="kotlin-stdlib-jdk8" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -250,6 +250,20 @@
|
||||
relative-to-action="Debugger.ShowReferring"/>
|
||||
</action>
|
||||
|
||||
<action class="com.intellij.debugger.memory.action.CalculateRetainedSizeAction"
|
||||
id="Memory.CalculateRetainedSize"
|
||||
text="Calculate Retained Size">
|
||||
<add-to-group group-id="XDebugger.ValueGroup" anchor="after"
|
||||
relative-to-action="MemoryView.ShowInstancesFromDebuggerTree"/>
|
||||
</action>
|
||||
|
||||
<action class="com.intellij.debugger.memory.action.ShowGarbageCollectorRootsAction"
|
||||
id="Memory.ShowGarbageCollectorRoots"
|
||||
text="Find Garbage Collector Roots">
|
||||
<add-to-group group-id="XDebugger.ValueGroup" anchor="after"
|
||||
relative-to-action="Memory.CalculateRetainedSize"/>
|
||||
</action>
|
||||
|
||||
<action class="com.intellij.debugger.memory.action.tracking.JumpToAllocationSourceAction"
|
||||
id="MemoryView.ShowAllocationStackTrace"
|
||||
text="Jump To Allocation Position">
|
||||
|
||||
Reference in New Issue
Block a user