mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Integrate memory agent into DebugProcessImpl
This commit is contained in:
@@ -13,6 +13,7 @@ import com.intellij.debugger.impl.DebuggerUtilsImpl;
|
||||
import com.intellij.debugger.impl.PrioritizedTask;
|
||||
import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
|
||||
import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
|
||||
import com.intellij.debugger.memory.agent.MemoryAgentManager;
|
||||
import com.intellij.debugger.requests.Requestor;
|
||||
import com.intellij.debugger.settings.DebuggerSettings;
|
||||
import com.intellij.debugger.ui.breakpoints.Breakpoint;
|
||||
@@ -364,6 +365,10 @@ public class DebugProcessEvents extends DebugProcessImpl {
|
||||
AsyncStacksUtils.setupAgent(this);
|
||||
}
|
||||
|
||||
if (canBeModified) {
|
||||
MemoryAgentManager.loadAgentProxy(this, agent -> myMemoryAgent = agent);
|
||||
}
|
||||
|
||||
// breakpoints should be initialized after all processAttached listeners work
|
||||
ApplicationManager.getApplication().runReadAction(() -> {
|
||||
XDebugSession session = getSession().getXDebugSession();
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.intellij.debugger.jdi.EmptyConnectorArgument;
|
||||
import com.intellij.debugger.jdi.StackFrameProxyImpl;
|
||||
import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
|
||||
import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
|
||||
import com.intellij.debugger.memory.agent.MemoryAgent;
|
||||
import com.intellij.debugger.settings.DebuggerSettings;
|
||||
import com.intellij.debugger.settings.NodeRendererSettings;
|
||||
import com.intellij.debugger.ui.breakpoints.BreakpointManager;
|
||||
@@ -103,6 +104,7 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb
|
||||
private final RequestManagerImpl myRequestManager;
|
||||
|
||||
private volatile VirtualMachineProxyImpl myVirtualMachineProxy = null;
|
||||
@Nullable protected volatile MemoryAgent myMemoryAgent;
|
||||
protected final EventDispatcher<DebugProcessListener> myDebugProcessDispatcher = EventDispatcher.create(DebugProcessListener.class);
|
||||
protected final EventDispatcher<EvaluationListener> myEvaluationDispatcher = EventDispatcher.create(EvaluationListener.class);
|
||||
|
||||
@@ -913,6 +915,11 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb
|
||||
return myDebuggerManagerThread;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public MemoryAgent getMemoryAgent() {
|
||||
return myMemoryAgent;
|
||||
}
|
||||
|
||||
private static int getInvokePolicy(SuspendContext suspendContext) {
|
||||
if (suspendContext.getSuspendPolicy() == EventRequest.SUSPEND_EVENT_THREAD ||
|
||||
isResumeOnlyCurrentThread() ||
|
||||
|
||||
+8
-8
@@ -1,8 +1,7 @@
|
||||
// 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.engine.evaluation.EvaluateException;
|
||||
import com.intellij.debugger.memory.agent.MemoryAgent;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.ui.messages.MessageDialog;
|
||||
@@ -11,19 +10,20 @@ 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,
|
||||
protected void perform(@NotNull MemoryAgent memoryAgent,
|
||||
@NotNull ObjectReference reference,
|
||||
@NotNull XValueNodeImpl node) {
|
||||
MemoryAgent memoryAgent = new AgentLoader().load(evaluationContext, evaluationContext.getDebugProcess().getVirtualMachineProxy());
|
||||
@NotNull XValueNodeImpl node) throws EvaluateException {
|
||||
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());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isEnabled(@NotNull MemoryAgent agent) {
|
||||
return agent.canEvaluateObjectSize();
|
||||
}
|
||||
}
|
||||
|
||||
+26
-25
@@ -2,20 +2,17 @@
|
||||
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.JavaDebugProcess;
|
||||
import com.intellij.debugger.engine.evaluation.EvaluateException;
|
||||
import com.intellij.debugger.engine.managerThread.DebuggerCommand;
|
||||
import com.intellij.debugger.memory.agent.MemoryAgent;
|
||||
import com.intellij.notification.NotificationType;
|
||||
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.XDebuggerManagerImpl;
|
||||
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 {
|
||||
@@ -24,20 +21,19 @@ public abstract class NativeAgentActionBase extends DebuggerTreeAction {
|
||||
@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;
|
||||
DebugProcessImpl debugProcess = JavaDebugProcess.getCurrentDebugProcess(project);
|
||||
ObjectReference reference = getObjectReference(node);
|
||||
if (debugProcess == null || reference == 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);
|
||||
MemoryAgent memoryAgent = debugProcess.getMemoryAgent();
|
||||
LOG.assertTrue(memoryAgent != null);
|
||||
try {
|
||||
perform(memoryAgent, reference, node);
|
||||
}
|
||||
catch (EvaluateException ex) {
|
||||
XDebuggerManagerImpl.NOTIFICATION_GROUP.createNotification("Action failed", NotificationType.ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,15 +47,20 @@ public abstract class NativeAgentActionBase extends DebuggerTreeAction {
|
||||
@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;
|
||||
DebugProcessImpl debugProcess = JavaDebugProcess.getCurrentDebugProcess(node.getTree().getProject());
|
||||
MemoryAgent memoryAgent = debugProcess == null ? null : debugProcess.getMemoryAgent();
|
||||
if (memoryAgent == null || !memoryAgent.isLoaded()) {
|
||||
e.getPresentation().setVisible(false);
|
||||
return false;
|
||||
}
|
||||
ObjectReference reference = getObjectReference(node);
|
||||
|
||||
return false;
|
||||
return reference != null && isEnabled(memoryAgent);
|
||||
}
|
||||
|
||||
protected abstract void perform(@NotNull EvaluationContextImpl evaluationContext,
|
||||
protected abstract boolean isEnabled(@NotNull MemoryAgent agent);
|
||||
|
||||
protected abstract void perform(@NotNull MemoryAgent agent,
|
||||
@NotNull ObjectReference reference,
|
||||
@NotNull XValueNodeImpl node);
|
||||
@NotNull XValueNodeImpl node) throws EvaluateException;
|
||||
}
|
||||
|
||||
+12
-9
@@ -3,14 +3,13 @@ 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.debugger.engine.evaluation.EvaluateException;
|
||||
import com.intellij.debugger.memory.agent.MemoryAgent;
|
||||
import com.intellij.notification.NotificationType;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.xdebugger.XDebugSession;
|
||||
import com.intellij.xdebugger.impl.XDebuggerManagerImpl;
|
||||
import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree;
|
||||
import com.intellij.xdebugger.impl.ui.tree.XInspectDialog;
|
||||
@@ -20,11 +19,9 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ShowGarbageCollectorRootsAction extends NativeAgentActionBase {
|
||||
@Override
|
||||
protected void perform(@NotNull EvaluationContextImpl evaluationContext,
|
||||
protected void perform(@NotNull MemoryAgent memoryAgent,
|
||||
@NotNull ObjectReference reference,
|
||||
@NotNull XValueNodeImpl node) {
|
||||
VirtualMachineProxyImpl virtualMachineProxy = evaluationContext.getDebugProcess().getVirtualMachineProxy();
|
||||
MemoryAgent memoryAgent = new AgentLoader().load(evaluationContext, virtualMachineProxy);
|
||||
@NotNull XValueNodeImpl node) throws EvaluateException {
|
||||
ReferringObjectsProvider roots = memoryAgent.canFindGcRoots() ? memoryAgent.findGcRoots(reference) : null;
|
||||
if (roots == null) {
|
||||
XDebuggerManagerImpl.NOTIFICATION_GROUP.createNotification("This feature is unavailable", NotificationType.INFORMATION);
|
||||
@@ -34,14 +31,20 @@ public class ShowGarbageCollectorRootsAction extends NativeAgentActionBase {
|
||||
() -> {
|
||||
XDebuggerTree tree = node.getTree();
|
||||
JavaValue javaValue = (JavaValue)node.getValueContainer();
|
||||
XDebugSession session = javaValue.getEvaluationContext().getDebugProcess().getSession().getXDebugSession();
|
||||
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);
|
||||
session, false);
|
||||
dialog.setTitle("Paths to GC Roots");
|
||||
dialog.show();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isEnabled(@NotNull MemoryAgent agent) {
|
||||
return agent.canFindGcRoots();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,20 +22,25 @@ import java.util.List;
|
||||
* @author Vitaliy.Bibaev
|
||||
*/
|
||||
public class AgentLoader {
|
||||
public static final MemoryAgent DEFAULT_PROXY = new MyDisabledMemoryAgent();
|
||||
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);
|
||||
if (classType != null) {
|
||||
MemoryAgentImpl agent = new MemoryAgentImpl(evaluationContext.getDebugProcess(), classType);
|
||||
agent.initializeCapabilities();
|
||||
return agent.isLoaded() ? agent : DEFAULT_PROXY;
|
||||
}
|
||||
}
|
||||
catch (EvaluateException e) {
|
||||
LOG.error("Could not load proxy class", e);
|
||||
return DEFAULT_PROXY;
|
||||
LOG.info("Could not load proxy class", e);
|
||||
}
|
||||
|
||||
return DEFAULT_PROXY;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -69,6 +74,11 @@ public class AgentLoader {
|
||||
}
|
||||
|
||||
private static class MyDisabledMemoryAgent implements MemoryAgent {
|
||||
@Override
|
||||
public boolean isLoaded() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canEvaluateObjectSize() {
|
||||
return false;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
package com.intellij.debugger.memory.agent;
|
||||
|
||||
import com.intellij.debugger.engine.ReferringObjectsProvider;
|
||||
import com.intellij.debugger.engine.evaluation.EvaluateException;
|
||||
import com.sun.jdi.ObjectReference;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -9,16 +10,18 @@ import org.jetbrains.annotations.Nullable;
|
||||
import java.util.List;
|
||||
|
||||
public interface MemoryAgent {
|
||||
boolean isLoaded();
|
||||
|
||||
boolean canEvaluateObjectSize();
|
||||
|
||||
long evaluateObjectSize(@NotNull ObjectReference reference);
|
||||
long evaluateObjectSize(@NotNull ObjectReference reference) throws EvaluateException;
|
||||
|
||||
boolean canEvaluateObjectsSizes();
|
||||
|
||||
List<Long> evaluateObjectsSizes(@NotNull List<ObjectReference> references);
|
||||
List<Long> evaluateObjectsSizes(@NotNull List<ObjectReference> references) throws EvaluateException;
|
||||
|
||||
boolean canFindGcRoots();
|
||||
|
||||
@Nullable
|
||||
ReferringObjectsProvider findGcRoots(@NotNull ObjectReference reference);
|
||||
ReferringObjectsProvider findGcRoots(@NotNull ObjectReference reference) throws EvaluateException;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
// 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.SuspendContextImpl;
|
||||
import com.intellij.debugger.engine.evaluation.EvaluateException;
|
||||
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
|
||||
import com.intellij.debugger.memory.agent.parsers.BooleanParser;
|
||||
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 com.sun.jdi.ClassType;
|
||||
import com.sun.jdi.Method;
|
||||
import com.sun.jdi.ObjectReference;
|
||||
import com.sun.jdi.Value;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -20,24 +27,37 @@ public class MemoryAgentImpl implements MemoryAgent {
|
||||
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 SIZE_OF_OBJECTS_METHOD_NAME = "sizes";
|
||||
private static final String GARBAGE_COLLECTOR_ROOTS_METHOD_NAME = "gcRoots";
|
||||
|
||||
private final EvaluationContextImpl myEvaluationContext;
|
||||
private final DebugProcessImpl myDebugProcess;
|
||||
private final ClassType myProxyClassType;
|
||||
|
||||
public MemoryAgentImpl(@NotNull EvaluationContextImpl context, @NotNull ClassType reference) {
|
||||
myEvaluationContext = context;
|
||||
private volatile boolean myIsLoaded;
|
||||
private volatile boolean myCanFindGcRoots;
|
||||
private volatile boolean myCanEstimateObjectSize;
|
||||
private volatile boolean myCanEstimateObjectsSizes;
|
||||
|
||||
public MemoryAgentImpl(@NotNull DebugProcessImpl debugProcess, @NotNull ClassType reference) {
|
||||
myDebugProcess = debugProcess;
|
||||
myProxyClassType = reference;
|
||||
}
|
||||
|
||||
public void initializeCapabilities() {
|
||||
DebuggerManagerThreadImpl.assertIsManagerThread();
|
||||
myIsLoaded = checkCapability("isLoaded");
|
||||
myCanFindGcRoots = checkCapability("canFindGcRoots");
|
||||
myCanEstimateObjectSize = checkCapability("canEstimateObjectSize");
|
||||
myCanEstimateObjectsSizes = checkCapability("canEstimateObjectsSizes");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canEvaluateObjectSize() {
|
||||
return !myProxyClassType.methodsByName(SIZE_OF_SINGLE_OBJECT_METHOD_NAME).isEmpty();
|
||||
return myCanEstimateObjectSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long evaluateObjectSize(@NotNull ObjectReference reference) {
|
||||
public long evaluateObjectSize(@NotNull ObjectReference reference) throws EvaluateException {
|
||||
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;
|
||||
@@ -45,11 +65,11 @@ public class MemoryAgentImpl implements MemoryAgent {
|
||||
|
||||
@Override
|
||||
public boolean canEvaluateObjectsSizes() {
|
||||
return !myProxyClassType.methodsByName(SIZE_OF_OBJECTS_METHOD_NAME).isEmpty();
|
||||
return myCanEstimateObjectsSizes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> evaluateObjectsSizes(@NotNull List<ObjectReference> references) {
|
||||
public List<Long> evaluateObjectsSizes(@NotNull List<ObjectReference> references) throws EvaluateException {
|
||||
if (!canEvaluateObjectsSizes()) throw new UnsupportedOperationException();
|
||||
Value result = callMethod(SIZE_OF_OBJECTS_METHOD_NAME, references);
|
||||
// TODO: Implement method and conversion
|
||||
@@ -58,41 +78,52 @@ public class MemoryAgentImpl implements MemoryAgent {
|
||||
|
||||
@Override
|
||||
public boolean canFindGcRoots() {
|
||||
return !myProxyClassType.methodsByName(GARBAGE_COLLECTOR_ROOTS_METHOD_NAME).isEmpty();
|
||||
return myCanFindGcRoots;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ReferringObjectsProvider findGcRoots(@NotNull ObjectReference reference) {
|
||||
public ReferringObjectsProvider findGcRoots(@NotNull ObjectReference reference) throws EvaluateException {
|
||||
if (!canFindGcRoots()) throw new UnsupportedOperationException();
|
||||
|
||||
Value value = callMethod(GARBAGE_COLLECTOR_ROOTS_METHOD_NAME, Collections.singletonList(reference));
|
||||
return value == null ? null : new GcRootsPathsParser().parse(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoaded() {
|
||||
return myIsLoaded;
|
||||
}
|
||||
|
||||
private boolean checkCapability(String methodName) {
|
||||
try {
|
||||
Value value = callMethod(methodName, Collections.emptyList());
|
||||
return value != null && BooleanParser.INSTANCE.parse(value);
|
||||
}
|
||||
catch (EvaluateException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Value callMethod(@NotNull String methodName,
|
||||
@NotNull List<? extends Value> args) {
|
||||
@NotNull List<? extends Value> args) throws EvaluateException {
|
||||
DebuggerManagerThreadImpl.assertIsManagerThread();
|
||||
List<Method> methods = myProxyClassType.methodsByName(methodName);
|
||||
if (methods.isEmpty()) {
|
||||
LOG.error("Method \"" + methodName + "\" not found");
|
||||
LOG.warn("Method \"" + methodName + "\" not found");
|
||||
return null;
|
||||
}
|
||||
if (methods.size() > 1) {
|
||||
LOG.warn("Too many methods \"" + methodName + "\" found. Count: " + methods.size());
|
||||
LOG.warn("Too many methods \"" + methodName + "\" found. Count: " + methods);
|
||||
}
|
||||
|
||||
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;
|
||||
SuspendContextImpl suspendContext = myDebugProcess.getSuspendManager().getPausedContext();
|
||||
EvaluationContextImpl evaluationContext = new EvaluationContextImpl(suspendContext, suspendContext.getFrameProxy());
|
||||
return myDebugProcess.invokeMethod(evaluationContext, myProxyClassType, method, args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,95 @@
|
||||
// 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.DebugProcessAdapterImpl;
|
||||
import com.intellij.debugger.engine.DebugProcessImpl;
|
||||
import com.intellij.debugger.engine.SuspendContextImpl;
|
||||
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
|
||||
import com.intellij.debugger.jdi.StackFrameProxyImpl;
|
||||
import com.intellij.execution.configurations.JavaParameters;
|
||||
import com.intellij.execution.configurations.ParametersList;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class MemoryAgentManager {
|
||||
private static final Logger LOG = Logger.getInstance(MemoryAgentManager.class);
|
||||
|
||||
public static void addMemoryAgent(JavaParameters parameters) {
|
||||
if (!Registry.is("debugger.enable.memory.agent")) return;
|
||||
ParametersList parametersList = parameters.getVMParametersList();
|
||||
if (parametersList.getParameters().stream().anyMatch(x -> x.contains("memory_agent"))) return;
|
||||
File extractedAgent = new AgentExtractor().extract();
|
||||
if (extractedAgent == null) {
|
||||
LOG.warn("Could not extract agent");
|
||||
if (!Registry.is("debugger.enable.memory.agent")) {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ParametersList parametersList = parameters.getVMParametersList();
|
||||
if (parametersList.getParameters().stream().anyMatch(x -> x.contains("memory_agent"))) return;
|
||||
File extractedAgent = null;
|
||||
String errorMessage = null;
|
||||
long start = System.currentTimeMillis();
|
||||
try {
|
||||
extractedAgent = ApplicationManager.getApplication()
|
||||
.executeOnPooledThread(() -> new AgentExtractor().extract()).get(1, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
errorMessage = "Interrupted";
|
||||
}
|
||||
catch (ExecutionException e) {
|
||||
LOG.warn(e.getCause());
|
||||
errorMessage = "Exception thrown (see logs for details)";
|
||||
}
|
||||
catch (TimeoutException e) {
|
||||
errorMessage = "Timeout";
|
||||
}
|
||||
if (errorMessage != null || extractedAgent == null) {
|
||||
LOG.warn("Could not extract agent: " + errorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG.info("Memory agent extracting took " + (System.currentTimeMillis() - start) + " ms");
|
||||
parametersList.add("-agentpath:" + extractedAgent.getAbsolutePath());
|
||||
}
|
||||
|
||||
public static void loadAgentProxy(@NotNull DebugProcessImpl debugProcess, @NotNull Consumer<MemoryAgent> agentLoaded) {
|
||||
debugProcess.addDebugProcessListener(new DebugProcessAdapterImpl() {
|
||||
@Override
|
||||
public void paused(SuspendContextImpl suspendContext) {
|
||||
MemoryAgent memoryAgent = initMemoryAgent(suspendContext);
|
||||
if (memoryAgent == null) {
|
||||
LOG.warn("Could not initialize memory agent.");
|
||||
return;
|
||||
}
|
||||
|
||||
agentLoaded.accept(memoryAgent);
|
||||
debugProcess.removeDebugProcessListener(this);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private MemoryAgent initMemoryAgent(@NotNull SuspendContextImpl suspendContext) {
|
||||
if (!Registry.is("debugger.enable.memory.agent")) {
|
||||
LOG.info("Memory agent disabled by registry key");
|
||||
return AgentLoader.DEFAULT_PROXY;
|
||||
}
|
||||
|
||||
StackFrameProxyImpl frameProxy = suspendContext.getFrameProxy();
|
||||
if (frameProxy == null) {
|
||||
LOG.warn("frame proxy is not available");
|
||||
return null;
|
||||
}
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
EvaluationContextImpl evaluationContext = new EvaluationContextImpl(suspendContext, frameProxy);
|
||||
MemoryAgent agent = new AgentLoader().load(evaluationContext, debugProcess.getVirtualMachineProxy());
|
||||
LOG.info("Memory agent loading took " + (System.currentTimeMillis() - start) + " ms");
|
||||
return agent;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
// 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.BooleanValue
|
||||
import com.sun.jdi.Value
|
||||
|
||||
object BooleanParser : ResultParser<Boolean> {
|
||||
override fun parse(value: Value): Boolean {
|
||||
return (value as? BooleanValue)?.value() ?: false
|
||||
}
|
||||
}
|
||||
@@ -14,8 +14,7 @@ class AgentExtractor {
|
||||
return@let if (SystemInfo.is32Bit) PlatformType.WINDOWS32 else PlatformType.WINDOWS64
|
||||
}
|
||||
|
||||
fun extract(): File? {
|
||||
// TODO: assert not EDT
|
||||
fun extract(): File {
|
||||
val file = FileUtil.createTempFile("${platform.prefix}memory_agent", platform.suffix, true)
|
||||
val agentFileName = "${platform.prefix}memory_agent${platform.suffix}"
|
||||
val inputStream = AgentExtractor::class.java.classLoader.getResourceAsStream("bin/$agentFileName")
|
||||
|
||||
Reference in New Issue
Block a user