IJ-CR-3115: Add agent actions timeouts

(cherry picked from commit 4cbeb8a4a1755d5dbfba9e3d44205d3c090630d7)

GitOrigin-RevId: 6225c47ce482c2ddba1a8fec98f11b8755f53700
This commit is contained in:
Nikita Nazarov
2020-10-07 12:56:40 +03:00
committed by intellij-monorepo-bot
parent 720609b80c
commit 4e9e87fda7
18 changed files with 518 additions and 126 deletions

View File

@@ -9,11 +9,13 @@ 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.memory.agent.MemoryAgent;
import com.intellij.debugger.memory.agent.MemoryAgentActionResult;
import com.intellij.debugger.memory.agent.ui.RetainedSizeDialog;
import com.intellij.notification.NotificationType;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.xdebugger.XDebuggerManager;
import com.intellij.xdebugger.impl.XDebuggerManagerImpl;
import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree;
@@ -46,11 +48,19 @@ public class CalculateRetainedSizeAction extends DebuggerTreeAction {
try {
EvaluationContextImpl evaluationContext = new EvaluationContextImpl(suspendContext, suspendContext.getFrameProxy());
MemoryAgent memoryAgent = MemoryAgent.get(debugProcess);
Pair<Long, ObjectReference[]> sizeAndHeldObjects = memoryAgent.estimateObjectSize(evaluationContext, reference);
dialog.setHeldObjectsAndRetainedSize(
Arrays.asList(sizeAndHeldObjects.getSecond()),
sizeAndHeldObjects.getFirst()
MemoryAgentActionResult<Pair<long[], ObjectReference[]>> result = memoryAgent.estimateObjectSize(
evaluationContext, reference, Registry.get("debugger.memory.agent.action.timeout").asInteger()
);
if (result.executedSuccessfully()) {
Pair<long[], ObjectReference[]> sizesAndHeldObjects = result.getResult();
dialog.setHeldObjectsAndSizes(
Arrays.asList(sizesAndHeldObjects.getSecond()),
sizesAndHeldObjects.getFirst()[0],
sizesAndHeldObjects.getFirst()[1]
);
} else {
dialog.setCalculationTimeout();
}
}
catch (EvaluateException e) {
XDebuggerManagerImpl.NOTIFICATION_GROUP.createNotification(JavaDebuggerBundle.message("action.failed"), NotificationType.ERROR);

View File

@@ -0,0 +1,91 @@
// Copyright 2000-2020 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.DebuggerContext;
import com.intellij.debugger.JavaDebuggerBundle;
import com.intellij.debugger.engine.ReferringObject;
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiExpression;
import com.intellij.xdebugger.frame.XFullValueEvaluator;
import com.intellij.xdebugger.frame.XValueNode;
import com.intellij.xdebugger.frame.presentation.XValuePresentation;
import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodePresentationConfigurator;
import com.sun.jdi.ObjectReference;
import com.sun.jdi.Value;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.function.Function;
public class CalculationTimeoutReferringObject implements ReferringObject {
@NotNull
@Override
public ValueDescriptorImpl createValueDescription(@NotNull Project project, @NotNull Value referee) {
return new ValueDescriptorImpl(project, null) {
@NotNull
@Override
public String getName() {
return "";
}
@Nullable
@Override
public Value calcValue(EvaluationContextImpl evaluationContext) {
return null;
}
@Nullable
@Override
public PsiExpression getDescriptorEvaluation(DebuggerContext context) {
return null;
}
};
}
@NotNull
@Override
public final Function<XValueNode, XValueNode> getNodeCustomizer() {
return node -> new XValueNodePresentationConfigurator.ConfigurableXValueNodeImpl() {
@Override
public void applyPresentation(@Nullable Icon icon, @NotNull final XValuePresentation valuePresenter, boolean hasChildren) {
node.setPresentation(icon, new XValuePresentation() {
@NotNull
@Override
public String getSeparator() {
return "";
}
@Nullable
@Override
public String getType() {
return null;
}
@Override
public void renderValue(@NotNull XValueTextRenderer renderer) {
renderer.renderValue(JavaDebuggerBundle.message("debugger.memory.agent.timeout.error"));
}
}, hasChildren);
}
@Override
public void setFullValueEvaluator(@NotNull XFullValueEvaluator fullValueEvaluator) {
}
};
}
@Nullable
@Override
public String getNodeName(int order) {
return null;
}
@Nullable
@Override
public ObjectReference getReference() {
return null;
}
}

View File

@@ -0,0 +1,97 @@
// Copyright 2000-2020 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.DebuggerContext;
import com.intellij.debugger.engine.ReferringObject;
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiExpression;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.xdebugger.frame.XFullValueEvaluator;
import com.intellij.xdebugger.frame.XValueNode;
import com.intellij.xdebugger.frame.presentation.XValuePresentation;
import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodePresentationConfigurator;
import com.sun.jdi.ObjectReference;
import com.sun.jdi.Value;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.Arrays;
import java.util.function.Function;
import java.util.stream.Collectors;
public class CompoundRootReferringObject implements ReferringObject {
private final MemoryAgentReferenceKind[] myKinds;
public CompoundRootReferringObject(MemoryAgentReferenceKind @NotNull[] kinds) {
myKinds = ContainerUtil.set(kinds).toArray(new MemoryAgentReferenceKind[0]);
}
@NotNull
@Override
public ValueDescriptorImpl createValueDescription(@NotNull Project project, @NotNull Value referee) {
return new ValueDescriptorImpl(project, null) {
@Override
public String getName() {
return "";
}
@Override
public Value calcValue(EvaluationContextImpl evaluationContext) {
return null;
}
@Override
public PsiExpression getDescriptorEvaluation(DebuggerContext context) {
return null;
}
};
}
@NotNull
@Override
public final Function<XValueNode, XValueNode> getNodeCustomizer() {
return node -> new XValueNodePresentationConfigurator.ConfigurableXValueNodeImpl() {
@Override
public void applyPresentation(@Nullable Icon icon, @NotNull final XValuePresentation valuePresenter, boolean hasChildren) {
node.setPresentation(AllIcons.Nodes.Record, new XValuePresentation() {
@NotNull
@Override
public String getSeparator() {
return ": ";
}
@Nullable
@Override
public String getType() {
return null;
}
@Override
public void renderValue(@NotNull XValueTextRenderer renderer) {
renderer.renderValue(Arrays.stream(myKinds).map(kind -> kind.toString() + " reference").collect(Collectors.joining(", ")));
}
}, hasChildren);
}
@Override
public void setFullValueEvaluator(@NotNull XFullValueEvaluator fullValueEvaluator) {
}
};
}
@NotNull
@Override
public String getNodeName(int order) {
return "Root";
}
@Nullable
@Override
public ObjectReference getReference() {
return null;
}
}

View File

@@ -20,7 +20,7 @@ import javax.swing.*;
import java.util.function.Function;
public class GCRootReferringObject implements ReferringObject {
@NotNull private final MemoryAgentReferenceKind myKind;
private final MemoryAgentReferenceKind myKind;
public GCRootReferringObject(@NotNull MemoryAgentReferenceKind kind) {
this.myKind = kind;
@@ -82,6 +82,11 @@ public class GCRootReferringObject implements ReferringObject {
};
}
@NotNull
public MemoryAgentReferenceKind getKind() {
return myKind;
}
@NotNull
@Override
public String getNodeName(int order) {

View File

@@ -0,0 +1,22 @@
// Copyright 2000-2020 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 org.jetbrains.annotations.NotNull;
public class JNILocalReferringObject extends GCRootReferringObject {
private final long myTid;
private final long myDepth;
public JNILocalReferringObject(@NotNull MemoryAgentReferenceKind kind,
long tid, long depth) {
super(kind);
this.myTid = tid;
this.myDepth = depth;
}
@NotNull
@Override
protected String getAdditionalInfo() {
return String.format("TID: %d DEPTH: %d", myTid, myDepth);
}
}

View File

@@ -33,22 +33,34 @@ public interface MemoryAgent {
@NotNull
MemoryAgentCapabilities capabilities();
Pair<Long, ObjectReference[]> estimateObjectSize(@NotNull EvaluationContextImpl evaluationContext, @NotNull ObjectReference reference) throws EvaluateException;
long[] estimateObjectsSizes(@NotNull EvaluationContextImpl evaluationContext, @NotNull List<ObjectReference> references)
throws EvaluateException;
long[] getShallowSizeByClasses(@NotNull EvaluationContextImpl evaluationContext, @NotNull List<ReferenceType> classes)
throws EvaluateException;
long[] getRetainedSizeByClasses(@NotNull EvaluationContextImpl evaluationContext, @NotNull List<ReferenceType> classes)
throws EvaluateException;
Pair<long[], long[]> getShallowAndRetainedSizeByClasses(@NotNull EvaluationContextImpl evaluationContext, @NotNull List<ReferenceType> classes)
throws EvaluateException;
@NotNull
MemoryAgentActionResult<Pair<long[], ObjectReference[]>> estimateObjectSize(@NotNull EvaluationContextImpl evaluationContext,
@NotNull ObjectReference reference,
long timeoutInMillis) throws EvaluateException;
@NotNull
ReferringObjectsInfo findPathsToClosestGCRoots(@NotNull EvaluationContextImpl evaluationContext, @NotNull ObjectReference reference,
int pathsNumber, int objectsNumber)
throws EvaluateException;
MemoryAgentActionResult<long[]> estimateObjectsSizes(@NotNull EvaluationContextImpl evaluationContext,
@NotNull List<ObjectReference> references,
long timeoutInMillis) throws EvaluateException;
@NotNull
MemoryAgentActionResult<long[]> getShallowSizeByClasses(@NotNull EvaluationContextImpl evaluationContext,
@NotNull List<ReferenceType> classes,
long timeoutInMillis) throws EvaluateException;
@NotNull
MemoryAgentActionResult<long[]> getRetainedSizeByClasses(@NotNull EvaluationContextImpl evaluationContext,
@NotNull List<ReferenceType> classes,
long timeoutInMillis) throws EvaluateException;
@NotNull
MemoryAgentActionResult<Pair<long[], long[]>> getShallowAndRetainedSizeByClasses(@NotNull EvaluationContextImpl evaluationContext,
@NotNull List<ReferenceType> classes,
long timeoutInMillis) throws EvaluateException;
@NotNull
MemoryAgentActionResult<ReferringObjectsInfo> findPathsToClosestGCRoots(@NotNull EvaluationContextImpl evaluationContext,
@NotNull ObjectReference reference,
int pathsNumber, int objectsNumber,
long timeoutInMillis) throws EvaluateException;
}

View File

@@ -0,0 +1,32 @@
// Copyright 2000-2020 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 org.jetbrains.annotations.NotNull;
public class MemoryAgentActionResult<T> {
public enum ErrorCode {
OK,
TIMEOUT;
public static ErrorCode valueOf(int value) {
return value == 0 ? OK : TIMEOUT;
}
}
private final ErrorCode myErrorCode;
private final T myResult;
public MemoryAgentActionResult(@NotNull T result, @NotNull ErrorCode errorCode) {
myErrorCode = errorCode;
myResult = result;
}
@NotNull
public T getResult() {
return myResult;
}
public boolean executedSuccessfully() {
return myErrorCode != ErrorCode.TIMEOUT;
}
}

View File

@@ -4,6 +4,7 @@ package com.intellij.debugger.memory.agent;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.registry.Registry;
import com.sun.jdi.ObjectReference;
import com.sun.jdi.ReferenceType;
import org.jetbrains.annotations.NotNull;
@@ -18,64 +19,78 @@ class MemoryAgentImpl implements MemoryAgent {
myCapabilities = capabilities;
}
@NotNull
@Override
public Pair<Long, ObjectReference[]> estimateObjectSize(@NotNull EvaluationContextImpl evaluationContext, @NotNull ObjectReference reference)
throws EvaluateException {
public MemoryAgentActionResult<Pair<long[], ObjectReference[]>> estimateObjectSize(@NotNull EvaluationContextImpl evaluationContext,
@NotNull ObjectReference reference,
long timeoutInMillis) throws EvaluateException {
if (!myCapabilities.canEstimateObjectSize()) {
throw new UnsupportedOperationException("Memory agent can't estimate object size");
}
return MemoryAgentOperations.estimateObjectSize(evaluationContext, reference);
return MemoryAgentOperations.estimateObjectSize(evaluationContext, reference, timeoutInMillis);
}
@NotNull
@Override
public long[] estimateObjectsSizes(@NotNull EvaluationContextImpl evaluationContext, @NotNull List<ObjectReference> references)
throws EvaluateException {
public MemoryAgentActionResult<long[]> estimateObjectsSizes(@NotNull EvaluationContextImpl evaluationContext,
@NotNull List<ObjectReference> references,
long timeoutInMillis) throws EvaluateException {
if (!myCapabilities.canEstimateObjectsSizes()) {
throw new UnsupportedOperationException("Memory agent can't estimate objects sizes");
}
return MemoryAgentOperations.estimateObjectsSizes(evaluationContext, references);
return MemoryAgentOperations.estimateObjectsSizes(evaluationContext, references, timeoutInMillis);
}
@NotNull
@Override
public long[] getShallowSizeByClasses(@NotNull EvaluationContextImpl evaluationContext, @NotNull List<ReferenceType> classes)
throws EvaluateException {
public MemoryAgentActionResult<long[]> getShallowSizeByClasses(@NotNull EvaluationContextImpl evaluationContext,
@NotNull List<ReferenceType> classes,
long timeoutInMillis) throws EvaluateException {
if (!myCapabilities.canGetShallowSizeByClasses()) {
throw new UnsupportedOperationException("Memory agent can't get shallow size by classes");
}
return MemoryAgentOperations.getShallowSizeByClasses(evaluationContext, classes);
return MemoryAgentOperations.getShallowSizeByClasses(evaluationContext, classes, timeoutInMillis);
}
@NotNull
@Override
public long[] getRetainedSizeByClasses(@NotNull EvaluationContextImpl evaluationContext, @NotNull List<ReferenceType> classes)
throws EvaluateException {
public MemoryAgentActionResult<long[]> getRetainedSizeByClasses(@NotNull EvaluationContextImpl evaluationContext,
@NotNull List<ReferenceType> classes,
long timeoutInMillis) throws EvaluateException {
if (!myCapabilities.canGetRetainedSizeByClasses()) {
throw new UnsupportedOperationException("Memory agent can't get retained size by classes");
}
return MemoryAgentOperations.getRetainedSizeByClasses(evaluationContext, classes);
return MemoryAgentOperations.getRetainedSizeByClasses(evaluationContext, classes, timeoutInMillis);
}
@NotNull
@Override
public Pair<long[], long[]> getShallowAndRetainedSizeByClasses(@NotNull EvaluationContextImpl evaluationContext, @NotNull List<ReferenceType> classes)
throws EvaluateException {
public MemoryAgentActionResult<Pair<long[], long[]>> getShallowAndRetainedSizeByClasses(@NotNull EvaluationContextImpl evaluationContext,
@NotNull List<ReferenceType> classes,
long timeoutInMillis) throws EvaluateException {
if (!myCapabilities.canGetRetainedSizeByClasses() || !myCapabilities.canGetShallowSizeByClasses()) {
throw new UnsupportedOperationException("Memory agent can't get shallow and retained size by classes");
}
return MemoryAgentOperations.getShallowAndRetainedSizeByClasses(evaluationContext, classes);
return MemoryAgentOperations.getShallowAndRetainedSizeByClasses(evaluationContext, classes, timeoutInMillis);
}
@NotNull
@Override
public @NotNull ReferringObjectsInfo findPathsToClosestGCRoots(@NotNull EvaluationContextImpl evaluationContext,
@NotNull ObjectReference reference,
int pathsNumber, int objectsNumber) throws EvaluateException {
public MemoryAgentActionResult<ReferringObjectsInfo> findPathsToClosestGCRoots(@NotNull EvaluationContextImpl evaluationContext,
@NotNull ObjectReference reference,
int pathsNumber,
int objectsNumber,
long timeoutInMillis) throws EvaluateException {
if (!myCapabilities.canFindPathsToClosestGcRoots()) {
throw new UnsupportedOperationException("Memory agent can't provide paths to closest gc roots");
}
return MemoryAgentOperations.findPathsToClosestGCRoots(evaluationContext, reference, pathsNumber, objectsNumber);
return MemoryAgentOperations.findPathsToClosestGCRoots(evaluationContext, reference, pathsNumber, objectsNumber, timeoutInMillis);
}
@NotNull

View File

@@ -26,57 +26,120 @@ final class MemoryAgentOperations {
private static final Key<MemoryAgent> MEMORY_AGENT_KEY = Key.create("MEMORY_AGENT_KEY");
private static final Logger LOG = Logger.getInstance(MemoryAgentOperations.class);
@NotNull
static Pair<Long, ObjectReference[]> estimateObjectSize(@NotNull EvaluationContextImpl evaluationContext, @NotNull ObjectReference reference)
throws EvaluateException {
Value result = callMethod(evaluationContext, MemoryAgentNames.Methods.ESTIMATE_OBJECT_SIZE, Collections.singletonList(reference));
Pair<Long, List<ObjectReference>> pair = SizeAndHeldObjectsParser.INSTANCE.parse(result);
return new Pair<>(pair.getFirst(), pair.getSecond().toArray(new ObjectReference[0]));
private static LongValue getTimeoutValue(@NotNull EvaluationContextImpl evaluationContext, long timeoutInMillis) {
return evaluationContext.getDebugProcess().getVirtualMachineProxy().mirrorOf(timeoutInMillis);
}
static long @NotNull [] estimateObjectsSizes(@NotNull EvaluationContextImpl evaluationContext, @NotNull List<ObjectReference> references)
throws EvaluateException {
@NotNull
static MemoryAgentActionResult<Pair<long[], ObjectReference[]>> estimateObjectSize(@NotNull EvaluationContextImpl evaluationContext,
@NotNull ObjectReference reference,
long timeoutInMillis) throws EvaluateException {
LongValue timeoutValue = getTimeoutValue(evaluationContext, timeoutInMillis);
Value result = callMethod(evaluationContext, MemoryAgentNames.Methods.ESTIMATE_OBJECT_SIZE, Arrays.asList(reference, timeoutValue));
Pair<MemoryAgentActionResult.ErrorCode, Value> errCodeAndResult = ErrorCodeParser.INSTANCE.parse(result);
MemoryAgentActionResult.ErrorCode errCode = errCodeAndResult.getFirst();
Pair<long[], ObjectReference[]> sizesAndObjects;
if (errCode != MemoryAgentActionResult.ErrorCode.OK) {
sizesAndObjects = new Pair<>(new long[0], new ObjectReference[0]);
} else {
Pair<Long[], ObjectReference[]> parsingResult = SizeAndHeldObjectsParser.INSTANCE.parse(errCodeAndResult.getSecond());
sizesAndObjects = new Pair<>(
Arrays.stream(parsingResult.getFirst()).mapToLong(Long::longValue).toArray(),
parsingResult.getSecond()
);
}
return new MemoryAgentActionResult<>(sizesAndObjects, errCode);
}
@NotNull
static MemoryAgentActionResult<long[]> estimateObjectsSizes(@NotNull EvaluationContextImpl evaluationContext,
@NotNull List<ObjectReference> references,
long timeoutInMillis) throws EvaluateException {
LongValue timeoutValue = getTimeoutValue(evaluationContext, timeoutInMillis);
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();
}
static long @NotNull [] getShallowSizeByClasses(@NotNull EvaluationContextImpl evaluationContext, @NotNull List<ReferenceType> classes)
throws EvaluateException {
ArrayReference array = wrapWithArray(evaluationContext, ContainerUtil.map(classes, ReferenceType::classObject));
Value result = callMethod(evaluationContext, MemoryAgentNames.Methods.GET_SHALLOW_SIZE_BY_CLASSES, Collections.singletonList(array));
return LongArrayParser.INSTANCE.parse(result).stream().mapToLong(Long::longValue).toArray();
}
static long @NotNull [] getRetainedSizeByClasses(@NotNull EvaluationContextImpl evaluationContext, @NotNull List<ReferenceType> classes)
throws EvaluateException {
ArrayReference array = wrapWithArray(evaluationContext, ContainerUtil.map(classes, ReferenceType::classObject));
Value result = callMethod(evaluationContext, MemoryAgentNames.Methods.GET_RETAINED_SIZE_BY_CLASSES, Collections.singletonList(array));
return LongArrayParser.INSTANCE.parse(result).stream().mapToLong(Long::longValue).toArray();
Value result = callMethod(evaluationContext, MemoryAgentNames.Methods.ESTIMATE_OBJECTS_SIZE, Arrays.asList(array, timeoutValue));
Pair<MemoryAgentActionResult.ErrorCode, Value> errCodeAndResult = ErrorCodeParser.INSTANCE.parse(result);
return new MemoryAgentActionResult<>(
LongArrayParser.INSTANCE.parse(errCodeAndResult.getSecond()).stream().mapToLong(Long::longValue).toArray(),
errCodeAndResult.getFirst()
);
}
@NotNull
static Pair<long[], long[]> getShallowAndRetainedSizeByClasses(@NotNull EvaluationContextImpl evaluationContext, @NotNull List<ReferenceType> classes)
throws EvaluateException {
static MemoryAgentActionResult<long[]> getShallowSizeByClasses(@NotNull EvaluationContextImpl evaluationContext,
@NotNull List<ReferenceType> classes,
long timeoutInMillis) throws EvaluateException {
LongValue timeoutValue = getTimeoutValue(evaluationContext, timeoutInMillis);
ArrayReference array = wrapWithArray(evaluationContext, ContainerUtil.map(classes, ReferenceType::classObject));
Value result = callMethod(evaluationContext, MemoryAgentNames.Methods.GET_SHALLOW_AND_RETAINED_SIZE_BY_CLASSES, Collections.singletonList(array));
Pair<List<Long>, List<Long>> pair = ShallowAndRetainedSizeParser.INSTANCE.parse(result);
return new Pair<>(pair.getFirst().stream().mapToLong(Long::longValue).toArray(),
pair.getSecond().stream().mapToLong(Long::longValue).toArray());
Value result = callMethod(evaluationContext, MemoryAgentNames.Methods.GET_SHALLOW_SIZE_BY_CLASSES, Arrays.asList(array, timeoutValue));
Pair<MemoryAgentActionResult.ErrorCode, Value> errCodeAndResult = ErrorCodeParser.INSTANCE.parse(result);
return new MemoryAgentActionResult<>(
LongArrayParser.INSTANCE.parse(errCodeAndResult.getSecond()).stream().mapToLong(Long::longValue).toArray(),
errCodeAndResult.getFirst()
);
}
@NotNull
static ReferringObjectsInfo findPathsToClosestGCRoots(@NotNull EvaluationContextImpl evaluationContext,
@NotNull ObjectReference reference, int pathsNumber,
int objectsNumber) throws EvaluateException {
static MemoryAgentActionResult<long[]> getRetainedSizeByClasses(@NotNull EvaluationContextImpl evaluationContext,
@NotNull List<ReferenceType> classes,
long timeoutInMillis) throws EvaluateException {
LongValue timeoutValue = getTimeoutValue(evaluationContext, timeoutInMillis);
ArrayReference array = wrapWithArray(evaluationContext, ContainerUtil.map(classes, ReferenceType::classObject));
Value result = callMethod(evaluationContext, MemoryAgentNames.Methods.GET_RETAINED_SIZE_BY_CLASSES, Arrays.asList(array, timeoutValue));
Pair<MemoryAgentActionResult.ErrorCode, Value> errCodeAndResult = ErrorCodeParser.INSTANCE.parse(result);
return new MemoryAgentActionResult<>(
LongArrayParser.INSTANCE.parse(errCodeAndResult.getSecond()).stream().mapToLong(Long::longValue).toArray(),
errCodeAndResult.getFirst()
);
}
@NotNull
static MemoryAgentActionResult<Pair<long[], long[]>> getShallowAndRetainedSizeByClasses(@NotNull EvaluationContextImpl evaluationContext,
@NotNull List<ReferenceType> classes,
long timeoutInMillis) throws EvaluateException {
LongValue timeoutValue = getTimeoutValue(evaluationContext, timeoutInMillis);
ArrayReference array = wrapWithArray(evaluationContext, ContainerUtil.map(classes, ReferenceType::classObject));
Value result = callMethod(evaluationContext, MemoryAgentNames.Methods.GET_SHALLOW_AND_RETAINED_SIZE_BY_CLASSES, Arrays.asList(array, timeoutValue));
Pair<MemoryAgentActionResult.ErrorCode, Value> errCodeAndResult = ErrorCodeParser.INSTANCE.parse(result);
Pair<List<Long>, List<Long>> shallowAndRetainedSizes = ShallowAndRetainedSizeParser.INSTANCE.parse(errCodeAndResult.getSecond());
return new MemoryAgentActionResult<>(
new Pair<>(
shallowAndRetainedSizes.getFirst().stream().mapToLong(Long::longValue).toArray(),
shallowAndRetainedSizes.getSecond().stream().mapToLong(Long::longValue).toArray()
),
errCodeAndResult.getFirst()
);
}
@NotNull
static MemoryAgentActionResult<ReferringObjectsInfo> findPathsToClosestGCRoots(@NotNull EvaluationContextImpl evaluationContext,
@NotNull ObjectReference reference, int pathsNumber,
int objectsNumber, long timeoutInMillis) throws EvaluateException {
LongValue timeoutValue = getTimeoutValue(evaluationContext, timeoutInMillis);
IntegerValue pathsNumberValue = evaluationContext.getDebugProcess().getVirtualMachineProxy().mirrorOf(pathsNumber);
IntegerValue objectsNumberValue = evaluationContext.getDebugProcess().getVirtualMachineProxy().mirrorOf(objectsNumber);
Value value = callMethod(
Value result = callMethod(
evaluationContext,
MemoryAgentNames.Methods.FIND_PATHS_TO_CLOSEST_GC_ROOTS,
Arrays.asList(reference, pathsNumberValue, objectsNumberValue)
Arrays.asList(reference, pathsNumberValue, objectsNumberValue, timeoutValue)
);
return GcRootsPathsParser.INSTANCE.parse(value);
Pair<MemoryAgentActionResult.ErrorCode, Value> errCodeAndResult = ErrorCodeParser.INSTANCE.parse(result);
MemoryAgentActionResult.ErrorCode errCode = errCodeAndResult.getFirst();
ReferringObjectsInfo returnValue;
if (errCode != MemoryAgentActionResult.ErrorCode.OK) {
returnValue = new ReferringObjectsInfo(
Collections.singletonList(reference),
Collections.singletonList(
Collections.singletonList(new CalculationTimeoutReferringObject())
)
);
} else {
returnValue = GcRootsPathsParser.INSTANCE.parse(errCodeAndResult.getSecond());
}
return new MemoryAgentActionResult<>(returnValue, errCodeAndResult.getFirst());
}
@NotNull

View File

@@ -5,6 +5,7 @@ import com.intellij.debugger.engine.ReferringObject;
import com.intellij.debugger.engine.ReferringObjectsProvider;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
import com.intellij.openapi.util.registry.Registry;
import com.sun.jdi.ObjectReference;
import org.jetbrains.annotations.NotNull;
@@ -49,6 +50,9 @@ public class MemoryAgentPathsToClosestGCRootsProvider implements ReferringObject
throw new UnsupportedOperationException();
}
return memoryAgent.findPathsToClosestGCRoots(evaluationContext, value, myPathsToRequestLimit, myObjectsToRequestLimit);
MemoryAgentActionResult<ReferringObjectsInfo> result = memoryAgent.findPathsToClosestGCRoots(
evaluationContext, value, myPathsToRequestLimit, myObjectsToRequestLimit, Registry.get("debugger.memory.agent.action.timeout").asInteger()
);
return result.getResult();
}
}

View File

@@ -137,7 +137,11 @@ public final class MemoryAgentUtil {
return objects;
}
try {
long[] sizes = agent.estimateObjectsSizes(context, ContainerUtil.map(objects, x -> x.getObjectReference()));
long[] sizes = agent.estimateObjectsSizes(
context,
ContainerUtil.map(objects, x -> x.getObjectReference()),
Registry.get("debugger.memory.agent.action.timeout").asInteger()
).getResult();
return IntStreamEx.range(0, objects.size())
.mapToObj(i -> new SizedReferenceInfo(objects.get(i).getObjectReference(), sizes[i]))
.reverseSorted(Comparator.comparing(x -> x.size()))
@@ -194,7 +198,7 @@ public final class MemoryAgentUtil {
}
return ApplicationManager.getApplication()
.executeOnPooledThread(() -> new AgentExtractor().extract(detectAgentKind(jdkPath), getAgentDirectory()))
.executeOnPooledThread(() -> AgentExtractor.INSTANCE.extract(detectAgentKind(jdkPath), getAgentDirectory()))
.get(1, TimeUnit.SECONDS);
}

View File

@@ -9,7 +9,7 @@ public class StackLocalReferringObject extends GCRootReferringObject {
private final String myMethodName;
public StackLocalReferringObject(@NotNull MemoryAgentReferenceKind kind,
String methodName,
@NotNull String methodName,
long tid, long depth) {
super(kind);
this.myTid = tid;
@@ -20,8 +20,6 @@ public class StackLocalReferringObject extends GCRootReferringObject {
@NotNull
@Override
protected String getAdditionalInfo() {
return String.format("%sTID: %d DEPTH: %d",
myMethodName != null ? String.format("from method: \"%s\" ", myMethodName) : "",
myTid, myDepth);
return String.format("in method: \"%s\" TID: %d DEPTH: %d", myMethodName, myTid, myDepth);
}
}

View File

@@ -5,6 +5,8 @@ import com.intellij.debugger.engine.ReferringObject
import com.intellij.debugger.memory.agent.*
import com.intellij.openapi.util.Pair
import com.sun.jdi.*
import java.util.*
import kotlin.collections.ArrayList
object StringParser : ResultParser<String> {
override fun parse(value: Value): String {
@@ -85,15 +87,11 @@ object ObjectsReferencesInfoParser : ResultParser<ReferringObjectsInfo> {
throw UnexpectedValueFormatException("Object references information should be represented by array")
val distinctIndices = mutableSetOf<Int>()
val referenceInfos = mutableListOf<ReferringObject>()
val referenceInfos = LinkedList<ReferringObject>()
val rootReferenceKinds = mutableListOf<MemoryAgentReferenceKind>()
for ((i, index) in indices.withIndex()) {
if (index == -1) {
referenceInfos.add(
MemoryAgentReferringObjectCreator.createRootReferringObject(
MemoryAgentReferenceKind.valueOf(kinds[i]),
infos.getValue(i)
)
)
rootReferenceKinds.add(MemoryAgentReferenceKind.valueOf(kinds[i]))
} else if (!distinctIndices.contains(index)) {
distinctIndices.add(index)
referenceInfos.add(
@@ -107,6 +105,9 @@ object ObjectsReferencesInfoParser : ResultParser<ReferringObjectsInfo> {
}
}
if (rootReferenceKinds.isNotEmpty()) {
referenceInfos.add(0, CompoundRootReferringObject(rootReferenceKinds.toTypedArray()))
}
result.add(referenceInfos)
}
@@ -142,13 +143,25 @@ object ShallowAndRetainedSizeParser : ResultParser<Pair<List<Long>, List<Long>>>
}
}
object SizeAndHeldObjectsParser : ResultParser<Pair<Long, List<ObjectReference>>> {
override fun parse(value: Value): Pair<Long, List<ObjectReference>> {
object SizeAndHeldObjectsParser : ResultParser<Pair<Array<Long>, Array<ObjectReference>>> {
override fun parse(value: Value): Pair<Array<Long>, Array<ObjectReference>> {
if (value !is ArrayReference) throw UnexpectedValueFormatException("Array expected")
if (value.length() < 2) throw UnexpectedValueFormatException("long and array of objects expected")
return Pair(
LongArrayParser.parse(value.getValue(0))[0],
ObjectReferencesParser.parse(value.getValue(1))
LongArrayParser.parse(value.getValue(0)).toTypedArray(),
ObjectReferencesParser.parse(value.getValue(1)).toTypedArray()
)
}
}
object ErrorCodeParser : ResultParser<Pair<MemoryAgentActionResult.ErrorCode, Value>> {
override fun parse(value: Value): Pair<MemoryAgentActionResult.ErrorCode, Value> {
if (value !is ArrayReference) throw UnexpectedValueFormatException("Array expected")
return Pair(
MemoryAgentActionResult.ErrorCode.valueOf(
IntArrayParser.parse(value.getValue(0))[0]
),
value.getValue(1)
)
}
}
@@ -173,12 +186,16 @@ object MemoryAgentReferringObjectCreator {
value: Value?): GCRootReferringObject {
return if (value == null) GCRootReferringObject(kind) else
when (kind) {
MemoryAgentReferenceKind.STACK_LOCAL,
MemoryAgentReferenceKind.STACK_LOCAL -> {
if (value !is ArrayReference) return GCRootReferringObject(kind)
val methodName = StringArrayParser.parse(value.getValue(1))[0] ?: return GCRootReferringObject(kind)
val longs = LongArrayParser.parse(value.getValue(0))
StackLocalReferringObject(kind, methodName, longs[0], longs[1])
}
MemoryAgentReferenceKind.JNI_LOCAL -> {
if (value !is ArrayReference) return GCRootReferringObject(kind)
val longs = LongArrayParser.parse(value.getValue(0))
val methodName = value.getValue(1)?.let { StringArrayParser.parse(it)[0] }
StackLocalReferringObject(kind, methodName, longs[0], longs[1])
JNILocalReferringObject(kind, longs[0], longs[1])
}
else -> GCRootReferringObject(kind)
}

View File

@@ -55,7 +55,7 @@ public class RetainedSizeDialog extends DialogWrapper {
private final BorderLayoutPanel myPanel;
private final NodeHighlighter myHighlighter;
private final String myRootName;
private final JBLabel myRetainedSizeLabel;
private JBLabel myRetainedSizeLabel;
public RetainedSizeDialog(@NotNull Project project,
XDebuggerEditorsProvider editorsProvider,
@@ -78,20 +78,9 @@ public class RetainedSizeDialog extends DialogWrapper {
myHeldObjects = new HashSet<>();
myRootName = name;
JBPanel topPanel = new JBPanel<>();
topPanel.setLayout(new VerticalFlowLayout());
myRetainedSizeLabel = new JBLabel(JavaDebuggerBundle.message("action.calculate.retained.size.waiting.message"));
topPanel.add(myRetainedSizeLabel);
topPanel.add(
new JBLabel(
JavaDebuggerBundle.message("action.calculate.retained.size.info", myRootName),
AllIcons.General.Information,
SwingConstants.LEFT
)
);
myPanel = JBUI.Panels.simplePanel()
.addToCenter(ScrollPaneFactory.createScrollPane(myTree))
.addToTop(topPanel);
.addToTop(createTopPanel());
if (session != null) {
session.addSessionListener(new XDebugSessionListener() {
@@ -112,7 +101,11 @@ public class RetainedSizeDialog extends DialogWrapper {
init();
}
public void setHeldObjectsAndRetainedSize(@NotNull Collection<? extends ObjectReference> heldObjects, long retainedSize) {
public void setCalculationTimeout() {
myRetainedSizeLabel.setText(JavaDebuggerBundle.message("debugger.memory.agent.timeout.error"));
}
public void setHeldObjectsAndSizes(@NotNull Collection<? extends ObjectReference> heldObjects, long shallowSize, long retainedSize) {
myHeldObjects.clear();
myHeldObjects.addAll(heldObjects);
highlightLoadedChildren();
@@ -120,6 +113,7 @@ public class RetainedSizeDialog extends DialogWrapper {
JavaDebuggerBundle.message(
"action.calculate.retained.size.text",
myRootName,
StringUtil.formatFileSize(shallowSize),
StringUtil.formatFileSize(retainedSize)
)
);
@@ -175,6 +169,22 @@ public class RetainedSizeDialog extends DialogWrapper {
}
}
@NotNull
private JBPanel createTopPanel() {
JBPanel panel = new JBPanel<>();
panel.setLayout(new VerticalFlowLayout());
myRetainedSizeLabel = new JBLabel(JavaDebuggerBundle.message("action.calculate.retained.size.waiting.message"));
panel.add(myRetainedSizeLabel);
panel.add(
new JBLabel(
JavaDebuggerBundle.message("action.calculate.retained.size.info", myRootName),
AllIcons.General.Information,
SwingConstants.LEFT
)
);
return panel;
}
private class NodeHighlighter implements XDebuggerTreeListener {
private boolean mySkipNotification;
private final Map<Icon, Icon> myCachedIcons;
@@ -188,10 +198,9 @@ public class RetainedSizeDialog extends DialogWrapper {
public void nodeLoaded(@NotNull RestorableStateNode node, @NotNull String name) {
if (!mySkipNotification && node instanceof XValueNodeImpl) {
XValueNodeImpl nodeImpl = (XValueNodeImpl)node;
if (myHeldObjects.contains(getObjectReference(nodeImpl))) {
if (nodeImpl != nodeImpl.getTree().getRoot() && myHeldObjects.contains(getObjectReference(nodeImpl))) {
XValuePresentation presentation = nodeImpl.getValuePresentation();
Icon icon = nodeImpl.getIcon();
if (presentation != null && icon != PlatformDebuggerImplIcons.PinToTop.UnpinnedItem) {
if (presentation != null && nodeImpl.getIcon() != PlatformDebuggerImplIcons.PinToTop.UnpinnedItem) {
highlightNode(nodeImpl);
}
}