[memory-agent] IDEA-CR-44694 Simplify lifetime of MemoryAgent instances (fixes for review)

This commit is contained in:
Vitaliy.Bibaev
2019-03-21 19:28:06 +03:00
parent 18f14240ed
commit b8e000aabb
12 changed files with 85 additions and 89 deletions
@@ -516,7 +516,7 @@ public class JavaValue extends XNamedValue implements NodeDescriptorProvider, XV
public XValue getReferringObjectsValue() {
ReferringObjectsProvider provider = ReferringObjectsProvider.BASIC_JDI;
if (MemoryAgent.capabilities(getEvaluationContext().getDebugProcess()).canGetReferringObjects()) {
if (MemoryAgent.get(getEvaluationContext().getDebugProcess()).capabilities().canGetReferringObjects()) {
provider = new MemoryAgentReferringObjectsProvider(MemoryAgent.DEFAULT_GC_ROOTS_OBJECTS_LIMIT);
}
return new JavaReferringObjectsValue(JavaValue.this, provider, null);
@@ -2,6 +2,7 @@
package com.intellij.debugger.memory.action;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
import com.intellij.debugger.memory.agent.MemoryAgent;
import com.intellij.debugger.memory.agent.MemoryAgentCapabilities;
import com.intellij.openapi.application.ApplicationManager;
@@ -13,10 +14,11 @@ import org.jetbrains.annotations.NotNull;
public class CalculateRetainedSizeAction extends MemoryAgentActionBase {
@Override
protected void perform(@NotNull MemoryAgent memoryAgent,
protected void perform(@NotNull EvaluationContextImpl evaluationContext,
@NotNull ObjectReference reference,
@NotNull XValueNodeImpl node) throws EvaluateException {
long size = memoryAgent.estimateObjectSize(reference);
MemoryAgent memoryAgent = MemoryAgent.get(evaluationContext.getDebugProcess());
long size = memoryAgent.estimateObjectSize(evaluationContext, reference);
ApplicationManager.getApplication().invokeLater(
() -> new MessageDialog(node.getTree().getProject(), String.valueOf(size), "Size of the Object",
ArrayUtil.EMPTY_STRING_ARRAY, 0, null, false)
@@ -3,8 +3,10 @@ package com.intellij.debugger.memory.action;
import com.intellij.debugger.engine.DebugProcessImpl;
import com.intellij.debugger.engine.JavaDebugProcess;
import com.intellij.debugger.engine.SuspendContextImpl;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.managerThread.DebuggerCommand;
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
import com.intellij.debugger.memory.agent.MemoryAgent;
import com.intellij.debugger.memory.agent.MemoryAgentCapabilities;
import com.intellij.notification.NotificationType;
@@ -25,16 +27,17 @@ public abstract class MemoryAgentActionBase extends DebuggerTreeAction {
DebugProcessImpl debugProcess = JavaDebugProcess.getCurrentDebugProcess(project);
ObjectReference reference = getObjectReference(node);
if (debugProcess == null || reference == null) return;
debugProcess.getManagerThread().invokeCommand(new DebuggerCommand() {
SuspendContextImpl suspendContext = debugProcess.getSuspendManager().getPausedContext();
debugProcess.getManagerThread().schedule(new SuspendContextCommandImpl(suspendContext) {
@Override
public void action() {
MemoryAgent memoryAgent = MemoryAgent.using(debugProcess);
if (memoryAgent == null) {
public void contextAction(@NotNull SuspendContextImpl suspendContext) {
EvaluationContextImpl evaluationContext = suspendContext.getEvaluationContext();
if (evaluationContext == null) {
LOG.error("Evaluation impossible");
return;
}
try {
perform(memoryAgent, reference, node);
perform(evaluationContext, reference, node);
}
catch (EvaluateException ex) {
XDebuggerManagerImpl.NOTIFICATION_GROUP.createNotification("Action failed", NotificationType.ERROR);
@@ -52,19 +55,19 @@ public abstract class MemoryAgentActionBase extends DebuggerTreeAction {
protected boolean isEnabled(@NotNull XValueNodeImpl node, @NotNull AnActionEvent e) {
if (!super.isEnabled(node, e)) return false;
DebugProcessImpl debugProcess = JavaDebugProcess.getCurrentDebugProcess(node.getTree().getProject());
if (debugProcess == null || !MemoryAgent.capabilities(debugProcess).isLoaded()) {
if (debugProcess == null || debugProcess.isEvaluationPossible() || !MemoryAgent.get(debugProcess).capabilities().isLoaded()) {
e.getPresentation().setVisible(false);
return false;
}
ObjectReference reference = getObjectReference(node);
return reference != null && isEnabled(MemoryAgent.capabilities(debugProcess));
return reference != null && isEnabled(MemoryAgent.get(debugProcess).capabilities());
}
protected abstract boolean isEnabled(@NotNull MemoryAgentCapabilities agentCapabilities);
protected abstract void perform(@NotNull MemoryAgent agent,
protected abstract void perform(@NotNull EvaluationContextImpl evaluationContext,
@NotNull ObjectReference reference,
@NotNull XValueNodeImpl node) throws EvaluateException;
}
@@ -2,12 +2,11 @@
package com.intellij.debugger.memory.agent;
import com.intellij.debugger.engine.DebugProcessImpl;
import com.intellij.debugger.engine.DebuggerManagerThreadImpl;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
import com.intellij.debugger.settings.DebuggerSettings;
import com.sun.jdi.ObjectReference;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
@@ -18,29 +17,21 @@ public interface MemoryAgent {
int DEFAULT_GC_ROOTS_OBJECTS_LIMIT = 1000;
@NotNull
static MemoryAgentCapabilities capabilities(@NotNull DebugProcessImpl debugProcess) {
return MemoryAgentCapabilities.get(debugProcess);
}
static MemoryAgent get(@NotNull DebugProcessImpl debugProcess) {
if (!DebuggerSettings.getInstance().ENABLE_MEMORY_AGENT) return MemoryAgentImpl.DISABLED;
@NotNull
static MemoryAgent using(@NotNull EvaluationContextImpl evaluationContext) {
DebuggerManagerThreadImpl.assertIsManagerThread();
return new MemoryAgentImpl(evaluationContext);
}
@Nullable
static MemoryAgent using(@NotNull DebugProcessImpl debugProcess) {
EvaluationContextImpl context = debugProcess.getDebuggerContext().createEvaluationContext();
return context != null ? using(context) : null;
return MemoryAgentOperations.getAgent(debugProcess);
}
@NotNull
MemoryAgentCapabilities capabilities();
long estimateObjectSize(@NotNull ObjectReference reference) throws EvaluateException;
long estimateObjectSize(@NotNull EvaluationContextImpl evaluationContext, @NotNull ObjectReference reference) throws EvaluateException;
long[] estimateObjectsSizes(@NotNull List<ObjectReference> references) throws EvaluateException;
long[] estimateObjectsSizes(@NotNull EvaluationContextImpl evaluationContext, @NotNull List<ObjectReference> references)
throws EvaluateException;
@NotNull
ReferringObjectsInfo findReferringObjects(@NotNull ObjectReference reference, int limit) throws EvaluateException;
ReferringObjectsInfo findReferringObjects(@NotNull EvaluationContextImpl evaluationContext, @NotNull ObjectReference reference, int limit)
throws EvaluateException;
}
@@ -1,9 +1,6 @@
// 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;
import com.intellij.debugger.engine.DebugProcessImpl;
import com.intellij.debugger.settings.DebuggerSettings;
import com.intellij.openapi.util.Key;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
@@ -11,8 +8,7 @@ import java.util.EnumSet;
import java.util.Set;
public class MemoryAgentCapabilities {
private static final Key<MemoryAgentCapabilities> MEMORY_AGENT_CAPABILITIES_KEY = Key.create("MEMORY_AGENT_CAPABILITIES_KEY");
public static final MemoryAgentCapabilities DISABLED = new MemoryAgentCapabilities(false, Collections.emptySet());
static final MemoryAgentCapabilities DISABLED = new MemoryAgentCapabilities(false, Collections.emptySet());
private final boolean myIsLoaded;
private final Set<Capability> myCapabilities;
@@ -23,17 +19,6 @@ public class MemoryAgentCapabilities {
myCapabilities.addAll(capabilitySet);
}
@NotNull
static MemoryAgentCapabilities get(@NotNull DebugProcessImpl debugProcess) {
if (!DebuggerSettings.getInstance().ENABLE_MEMORY_AGENT) return DISABLED;
MemoryAgentCapabilities capabilities = debugProcess.getUserData(MEMORY_AGENT_CAPABILITIES_KEY);
return capabilities != null ? capabilities : DISABLED;
}
static void set(@NotNull DebugProcessImpl debugProcess, MemoryAgentCapabilities capabilities) {
debugProcess.putUserData(MEMORY_AGENT_CAPABILITIES_KEY, capabilities);
}
public boolean isLoaded() {
return myIsLoaded;
}
@@ -9,31 +9,47 @@ import org.jetbrains.annotations.NotNull;
import java.util.List;
class MemoryAgentImpl implements MemoryAgent {
private final EvaluationContextImpl myEvaluationContext;
static final MemoryAgent DISABLED = new MemoryAgentImpl(MemoryAgentCapabilities.DISABLED);
private final MemoryAgentCapabilities myCapabilities;
MemoryAgentImpl(@NotNull EvaluationContextImpl evaluationContext) {
myEvaluationContext = evaluationContext;
MemoryAgentImpl(@NotNull MemoryAgentCapabilities capabilities) {
myCapabilities = capabilities;
}
@Override
public long estimateObjectSize(@NotNull ObjectReference reference) throws EvaluateException {
return MemoryAgentOperations.estimateObjectSize(myEvaluationContext, reference);
public long estimateObjectSize(@NotNull EvaluationContextImpl evaluationContext, @NotNull ObjectReference reference)
throws EvaluateException {
if (!myCapabilities.canEstimateObjectSize()) {
throw new UnsupportedOperationException("Memory agent can't estimate object size");
}
return MemoryAgentOperations.estimateObjectSize(evaluationContext, reference);
}
@Override
public long[] estimateObjectsSizes(@NotNull List<ObjectReference> references) throws EvaluateException {
return MemoryAgentOperations.estimateObjectsSizes(myEvaluationContext, references);
public long[] estimateObjectsSizes(@NotNull EvaluationContextImpl evaluationContext, @NotNull List<ObjectReference> references)
throws EvaluateException {
if (!myCapabilities.canEstimateObjectsSizes()) {
throw new UnsupportedOperationException("Memory agent can't estimate objects sizes");
}
return MemoryAgentOperations.estimateObjectsSizes(evaluationContext, references);
}
@NotNull
@Override
public ReferringObjectsInfo findReferringObjects(@NotNull ObjectReference reference, int limit) throws EvaluateException {
return MemoryAgentOperations.findReferringObjects(myEvaluationContext, reference, limit);
public ReferringObjectsInfo findReferringObjects(@NotNull EvaluationContextImpl evaluationContext,
@NotNull ObjectReference reference,
int limit) throws EvaluateException {
if (!myCapabilities.canGetReferringObjects()) {
throw new UnsupportedOperationException("Memory agent can't provide referring objects");
}
return MemoryAgentOperations.findReferringObjects(evaluationContext, reference, limit);
}
@NotNull
@Override
public MemoryAgentCapabilities capabilities() {
return MemoryAgent.capabilities(myEvaluationContext.getDebugProcess());
return myCapabilities;
}
}
@@ -9,7 +9,7 @@ interface MemoryAgentNames {
String CAN_FIND_GC_ROOTS = "canFindGcRoots";
String CAN_ESTIMATE_OBJECT_SIZE = "canEstimateObjectSize";
String CAN_ESTIMATE_OBJECTS_SIZES = "canEstimateObjectSize";
String CAN_ESTIMATE_OBJECTS_SIZES = "canEstimateObjectsSizes";
String ESTIMATE_OBJECT_SIZE = "size";
String ESTIMATE_OBJECTS_SIZE = "estimateRetainedSize";
@@ -13,6 +13,7 @@ import com.intellij.debugger.memory.agent.parsers.GcRootsPathsParser;
import com.intellij.debugger.memory.agent.parsers.LongArrayParser;
import com.intellij.debugger.memory.agent.parsers.LongValueParser;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Key;
import com.sun.jdi.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -22,14 +23,11 @@ import java.util.Collections;
import java.util.List;
class MemoryAgentOperations {
private static final Key<MemoryAgent> MEMORY_AGENT_KEY = Key.create("MEMORY_AGENT_KEY");
private static final Logger LOG = Logger.getInstance(MemoryAgentOperations.class);
static long estimateObjectSize(@NotNull EvaluationContextImpl evaluationContext, @NotNull ObjectReference reference)
throws EvaluateException {
if (!capabilities(evaluationContext).canEstimateObjectSize()) {
throw new UnsupportedOperationException("Memory agent can't estimate object size");
}
Value result = callMethod(evaluationContext, MemoryAgentNames.Methods.ESTIMATE_OBJECT_SIZE, Collections.singletonList(reference));
return LongValueParser.INSTANCE.parse(result);
}
@@ -37,10 +35,6 @@ class MemoryAgentOperations {
@NotNull
static long[] estimateObjectsSizes(@NotNull EvaluationContextImpl evaluationContext, @NotNull List<ObjectReference> references)
throws EvaluateException {
if (!capabilities(evaluationContext).canEstimateObjectsSizes()) {
throw new UnsupportedOperationException("Memory agent can't estimate objects sizes");
}
ArrayReference array = wrapWithArray(evaluationContext, references);
Value result = callMethod(evaluationContext, MemoryAgentNames.Methods.ESTIMATE_OBJECTS_SIZE, Collections.singletonList(array));
return LongArrayParser.INSTANCE.parse(result).stream().mapToLong(Long::longValue).toArray();
@@ -49,40 +43,42 @@ class MemoryAgentOperations {
@NotNull
static ReferringObjectsInfo findReferringObjects(@NotNull EvaluationContextImpl evaluationContext,
@NotNull ObjectReference reference, int limit) throws EvaluateException {
if (!capabilities(evaluationContext).canGetReferringObjects()) {
throw new UnsupportedOperationException("Memory agent can't provide referring objects");
}
IntegerValue limitValue = evaluationContext.getDebugProcess().getVirtualMachineProxy().mirrorOf(limit);
Value value = callMethod(evaluationContext, MemoryAgentNames.Methods.FIND_GC_ROOTS, Arrays.asList(reference, limitValue));
return GcRootsPathsParser.INSTANCE.parse(value);
}
static void initializeCapabilities(@NotNull EvaluationContextImpl context) {
@NotNull
static MemoryAgent getAgent(@NotNull DebugProcessImpl debugProcess) {
MemoryAgent agent = debugProcess.getUserData(MEMORY_AGENT_KEY);
return agent == null ? MemoryAgentImpl.DISABLED : agent;
}
static void initializeAgent(@NotNull EvaluationContextImpl context) {
DebuggerManagerThreadImpl.assertIsManagerThread();
MemoryAgent agent = MemoryAgentImpl.DISABLED;
try {
initialize(context);
agent = new MemoryAgentImpl(initializeCapabilities(context));
}
catch (EvaluateException e) {
LOG.error("Could not initialize memory agent. ", e);
MemoryAgentCapabilities.set(context.getDebugProcess(), MemoryAgentCapabilities.DISABLED);
}
context.getDebugProcess().putUserData(MEMORY_AGENT_KEY, agent);
}
private static void initialize(@NotNull EvaluationContextImpl context) throws EvaluateException {
private static MemoryAgentCapabilities initializeCapabilities(@NotNull EvaluationContextImpl context) throws EvaluateException {
ClassType proxyType = getProxyType(context);
boolean isAgentLoaded = checkAgentCapability(context, proxyType, MemoryAgentNames.Methods.IS_LOADED);
if (!isAgentLoaded) {
MemoryAgentCapabilities.set(context.getDebugProcess(), MemoryAgentCapabilities.DISABLED);
return MemoryAgentCapabilities.DISABLED;
}
else {
MemoryAgentCapabilities.Builder builder = new MemoryAgentCapabilities.Builder();
MemoryAgentCapabilities capabilities = builder
return builder
.setCanEstimateObjectSize(checkAgentCapability(context, proxyType, MemoryAgentNames.Methods.CAN_ESTIMATE_OBJECT_SIZE))
.setCanEstimateObjectsSizes(checkAgentCapability(context, proxyType, MemoryAgentNames.Methods.CAN_ESTIMATE_OBJECTS_SIZES))
.setCanFindGcRoots(checkAgentCapability(context, proxyType, MemoryAgentNames.Methods.CAN_FIND_GC_ROOTS))
.buildLoaded();
MemoryAgentCapabilities.set(context.getDebugProcess(), capabilities);
}
}
@@ -196,8 +192,4 @@ class MemoryAgentOperations {
LOG.info("Wrapping values with array took " + (System.currentTimeMillis() - start) + " ms");
return instancesArray;
}
private static MemoryAgentCapabilities capabilities(@NotNull EvaluationContextImpl evaluationContext) {
return MemoryAgent.capabilities(evaluationContext.getDebugProcess());
}
}
@@ -35,12 +35,12 @@ public class MemoryAgentReferringObjectsProvider implements ReferringObjectsProv
}
}
MemoryAgent memoryAgent = MemoryAgent.using(evaluationContext);
MemoryAgent memoryAgent = MemoryAgent.get(evaluationContext.getDebugProcess());
if (memoryAgent.capabilities().canGetReferringObjects()) {
throw new UnsupportedOperationException();
}
ReferringObjectsInfo roots = memoryAgent.findReferringObjects(value, myObjectsToRequestLimit);
ReferringObjectsInfo roots = memoryAgent.findReferringObjects(evaluationContext, value, myObjectsToRequestLimit);
myCachedRequests.put(value, roots);
return roots.getReferringObjects(value, limit);
}
@@ -42,7 +42,6 @@ import com.intellij.util.containers.ContainerUtil;
import one.util.streamex.IntStreamEx;
import one.util.streamex.StreamEx;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.model.java.JdkVersionDetector;
import javax.swing.event.HyperlinkEvent;
@@ -70,6 +69,11 @@ public class MemoryAgentUtil {
return;
}
//if(isOsSupported()) {
// LOG.info(SystemInfo.);
//
//}
if (isIbmJdk(parameters)) {
LOG.info("Do not attach memory agent for IBM jdk");
return;
@@ -119,14 +123,17 @@ public class MemoryAgentUtil {
listenIfStartupFailed();
}
public static List<JavaReferenceInfo> tryCalculateSizes(@NotNull List<JavaReferenceInfo> objects, @Nullable MemoryAgent agent) {
if (agent == null || !agent.capabilities().canEstimateObjectsSizes()) return objects;
@NotNull
public static List<JavaReferenceInfo> tryCalculateSizes(@NotNull EvaluationContextImpl context,
@NotNull List<JavaReferenceInfo> objects) {
MemoryAgent agent = MemoryAgent.get(context.getDebugProcess());
if (!agent.capabilities().canEstimateObjectsSizes()) return objects;
if (objects.size() > ESTIMATE_OBJECTS_SIZE_LIMIT) {
LOG.info("Too many objects to estimate their sizes");
return objects;
}
try {
long[] sizes = agent.estimateObjectsSizes(ContainerUtil.map(objects, x -> x.getObjectReference()));
long[] sizes = agent.estimateObjectsSizes(context, ContainerUtil.map(objects, x -> x.getObjectReference()));
return IntStreamEx.range(0, objects.size())
.mapToObj(i -> new SizedReferenceInfo(objects.get(i).getObjectReference(), sizes[i]))
.reverseSorted(Comparator.comparing(x -> x.size()))
@@ -151,7 +158,7 @@ public class MemoryAgentUtil {
if (context.isEvaluationPossible()) {
if (isInitialized.compareAndSet(false, true)) {
debugProcess.removeDebugProcessListener(this);
MemoryAgentOperations.initializeCapabilities(context);
MemoryAgentOperations.initializeAgent(context);
}
}
}
@@ -195,7 +195,7 @@ class InstancesView extends InstancesViewBase {
}
if (Registry.is("debugger.memory.agent.use.in.memory.view")) {
instances = MemoryAgentUtil.tryCalculateSizes(instances, MemoryAgent.using(evaluationContext));
instances = MemoryAgentUtil.tryCalculateSizes(evaluationContext, instances);
}
synchronized (myFilteringTaskLock) {
@@ -65,7 +65,7 @@ public class DebuggerSettings implements Cloneable, PersistentStateComponent<Ele
public boolean DISABLE_JIT;
public boolean SHOW_ALTERNATIVE_SOURCE = true;
public boolean HOTSWAP_IN_BACKGROUND = true;
public boolean ENABLE_MEMORY_AGENT =
public volatile boolean ENABLE_MEMORY_AGENT =
ApplicationManager.getApplication().isEAP() && !ApplicationManager.getApplication().isUnitTestMode();
public boolean ALWAYS_SMART_STEP_INTO = true;
public boolean SKIP_SYNTHETIC_METHODS = true;