From 33e77658d195216662384d5f5daa88f5ada0fce9 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Thu, 17 Jan 2019 12:19:28 +0300 Subject: [PATCH] Integrate memory agent into DebugProcessImpl --- .../debugger/engine/DebugProcessEvents.java | 5 ++ .../debugger/engine/DebugProcessImpl.java | 7 ++ .../action/CalculateRetainedSizeAction.java | 16 ++-- .../memory/action/NativeAgentActionBase.java | 51 ++++++------ .../ShowGarbageCollectorRootsAction.java | 21 +++-- .../debugger/memory/agent/AgentLoader.java | 18 +++- .../debugger/memory/agent/MemoryAgent.java | 9 +- .../memory/agent/MemoryAgentImpl.java | 75 ++++++++++++----- .../memory/agent/MemoryAgentManager.java | 82 +++++++++++++++++-- .../memory/agent/parsers/BooleanParser.kt | 11 +++ .../debugger/memory/agent/AgentExtractor.kt | 3 +- 11 files changed, 219 insertions(+), 79 deletions(-) create mode 100644 java/debugger/impl/src/com/intellij/debugger/memory/agent/parsers/BooleanParser.kt diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessEvents.java b/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessEvents.java index e92d6c0759ca..b147e2df568f 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessEvents.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessEvents.java @@ -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(); diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessImpl.java index bbd3a906571b..a27251cc58b7 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessImpl.java @@ -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 myDebugProcessDispatcher = EventDispatcher.create(DebugProcessListener.class); protected final EventDispatcher 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() || diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/action/CalculateRetainedSizeAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/CalculateRetainedSizeAction.java index b08f8c8f5436..846373d65348 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/action/CalculateRetainedSizeAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/action/CalculateRetainedSizeAction.java @@ -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(); + } } diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/action/NativeAgentActionBase.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/NativeAgentActionBase.java index abcb0f1a63a1..a1179d514a28 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/action/NativeAgentActionBase.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/action/NativeAgentActionBase.java @@ -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; } diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowGarbageCollectorRootsAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowGarbageCollectorRootsAction.java index 7319990dea29..c5c9aec1ee2f 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowGarbageCollectorRootsAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowGarbageCollectorRootsAction.java @@ -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(); + } } diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/agent/AgentLoader.java b/java/debugger/impl/src/com/intellij/debugger/memory/agent/AgentLoader.java index abe8a62041e2..d0deb75b195c 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/agent/AgentLoader.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/agent/AgentLoader.java @@ -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; diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/agent/MemoryAgent.java b/java/debugger/impl/src/com/intellij/debugger/memory/agent/MemoryAgent.java index a7efb76bf3e6..c6ec09914f2a 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/agent/MemoryAgent.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/agent/MemoryAgent.java @@ -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 evaluateObjectsSizes(@NotNull List references); + List evaluateObjectsSizes(@NotNull List references) throws EvaluateException; boolean canFindGcRoots(); @Nullable - ReferringObjectsProvider findGcRoots(@NotNull ObjectReference reference); + ReferringObjectsProvider findGcRoots(@NotNull ObjectReference reference) throws EvaluateException; } diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/agent/MemoryAgentImpl.java b/java/debugger/impl/src/com/intellij/debugger/memory/agent/MemoryAgentImpl.java index f4c77b2bda32..3149a1f18ace 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/agent/MemoryAgentImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/agent/MemoryAgentImpl.java @@ -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 evaluateObjectsSizes(@NotNull List references) { + public List evaluateObjectsSizes(@NotNull List 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 args) { + @NotNull List args) throws EvaluateException { + DebuggerManagerThreadImpl.assertIsManagerThread(); List 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); } } diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/agent/MemoryAgentManager.java b/java/debugger/impl/src/com/intellij/debugger/memory/agent/MemoryAgentManager.java index 7d7dd0752594..1e0ad86d8e2b 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/agent/MemoryAgentManager.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/agent/MemoryAgentManager.java @@ -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 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; + } + }); + } } diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/agent/parsers/BooleanParser.kt b/java/debugger/impl/src/com/intellij/debugger/memory/agent/parsers/BooleanParser.kt new file mode 100644 index 000000000000..9ce79924d419 --- /dev/null +++ b/java/debugger/impl/src/com/intellij/debugger/memory/agent/parsers/BooleanParser.kt @@ -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 { + override fun parse(value: Value): Boolean { + return (value as? BooleanValue)?.value() ?: false + } +} \ No newline at end of file diff --git a/java/debugger/memory-agent/src/com/intellij/debugger/memory/agent/AgentExtractor.kt b/java/debugger/memory-agent/src/com/intellij/debugger/memory/agent/AgentExtractor.kt index ae1b8b88a88c..99df9d980eb5 100644 --- a/java/debugger/memory-agent/src/com/intellij/debugger/memory/agent/AgentExtractor.kt +++ b/java/debugger/memory-agent/src/com/intellij/debugger/memory/agent/AgentExtractor.kt @@ -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")