capture agent: avoid key evaluation on IDE side

we could get the captured stack for the current place using only CURRENT_STACKS queue;
also it allows to drop insertion points list in IDE (and matching), this will simplify handling of stand-alone agent in IDEA-194359
This commit is contained in:
Egor Ushakov
2018-06-29 17:58:54 +03:00
parent ef78e7e496
commit f8ab6be30a
9 changed files with 92 additions and 130 deletions
@@ -214,11 +214,20 @@ public class CaptureStorage {
}
}
// to be run from the debugger
@SuppressWarnings("unused")
public static Object[][] getCurrentCapturedStack(int limit) {
return wrapInArray(CURRENT_STACKS.get().peekLast(), limit);
}
// to be run from the debugger
@SuppressWarnings("unused")
public static Object[][] getRelatedStack(Object key, int limit) {
//noinspection SuspiciousMethodCalls
CapturedStack stack = STORAGE.get(new HardKey(key));
return wrapInArray(STORAGE.get(new HardKey(key)), limit);
}
private static Object[][] wrapInArray(CapturedStack stack, int limit) {
if (stack == null) {
return null;
}
@@ -22,6 +22,7 @@
<orderEntry type="module" module-name="intellij.platform.ide.impl" />
<orderEntry type="module" module-name="intellij.platform.util" />
<orderEntry type="module" module-name="intellij.platform.jps.build" />
<orderEntry type="module" module-name="intellij.java.debugger.agent.storage" scope="PROVIDED" />
</component>
<component name="copyright">
<Base>
@@ -17,7 +17,6 @@ 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.settings.CapturePoint;
import com.intellij.debugger.settings.DebuggerSettings;
import com.intellij.debugger.settings.NodeRendererSettings;
import com.intellij.debugger.ui.breakpoints.BreakpointManager;
@@ -133,8 +132,6 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb
private final ThreadBlockedMonitor myThreadBlockedMonitor = new ThreadBlockedMonitor(this, myDisposable);
private List<CapturePoint> myAgentInsertPoints = Collections.emptyList();
protected DebugProcessImpl(Project project) {
myProject = project;
myDebuggerManagerThread = new DebuggerManagerThreadImpl(myDisposable, myProject);
@@ -2203,13 +2200,4 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb
static boolean isResumeOnlyCurrentThread() {
return DebuggerSettings.getInstance().RESUME_ONLY_CURRENT_THREAD;
}
@NotNull
public List<CapturePoint> getAgentInsertPoints() {
return myAgentInsertPoints;
}
public void setAgentInsertPoints(@NotNull List<CapturePoint> agentInsertPoints) {
myAgentInsertPoints = agentInsertPoints;
}
}
@@ -18,7 +18,6 @@ import com.intellij.icons.AllIcons;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.ui.ColoredTextContainer;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.xdebugger.frame.XExecutionStack;
import com.intellij.xdebugger.frame.XStackFrame;
import com.intellij.xdebugger.impl.XDebugSessionImpl;
@@ -142,7 +141,8 @@ public class JavaExecutionStack extends XExecutionStack {
iterator.next();
added++;
}
myDebugProcess.getManagerThread().schedule(new AppendFrameCommand(suspendContext, iterator, container, added, firstFrameIndex));
myDebugProcess.getManagerThread().schedule(
new AppendFrameCommand(suspendContext, iterator, container, added, firstFrameIndex, null));
}
catch (EvaluateException e) {
container.errorOccurred(e.getMessage());
@@ -161,17 +161,20 @@ public class JavaExecutionStack extends XExecutionStack {
private final XStackFrameContainer myContainer;
private int myAdded;
private final int mySkip;
private final List<StackFrameItem> myAsyncStack;
public AppendFrameCommand(SuspendContextImpl suspendContext,
Iterator<StackFrameProxyImpl> stackFramesIterator,
XStackFrameContainer container,
int added,
int skip) {
int skip,
List<StackFrameItem> asyncStack) {
super(suspendContext);
myStackFramesIterator = stackFramesIterator;
myContainer = container;
myAdded = added;
mySkip = skip;
myAsyncStack = asyncStack;
}
@Override
@@ -209,49 +212,58 @@ public class JavaExecutionStack extends XExecutionStack {
}
// replace the rest with the related stack (if available)
if (frame instanceof JavaStackFrame
&& AsyncStacksToggleAction.isAsyncStacksEnabled((XDebugSessionImpl)myDebugProcess.getXdebugProcess().getSession())) {
List<StackFrameItem> relatedStack = StackCapturingLineBreakpoint.getRelatedStack(frameProxy, suspendContext, true);
if (!ContainerUtil.isEmpty(relatedStack)) {
int i = 0;
boolean separator = true;
for (StackFrameItem stackFrame : relatedStack) {
if (i > StackCapturingLineBreakpoint.getMaxStackLength()) {
addFrameIfNeeded(new XStackFrame() {
@Override
public void customizePresentation(@NotNull ColoredTextContainer component) {
component.append("Too many frames, the rest is truncated...", SimpleTextAttributes.REGULAR_ITALIC_ATTRIBUTES);
}
}, true);
return;
}
i++;
if (stackFrame == null) {
separator = true;
continue;
}
StackFrameItem.CapturedStackFrame newFrame = stackFrame.createFrame(myDebugProcess);
if (showFrame(newFrame)) {
newFrame.setWithSeparator(separator);
addFrameIfNeeded(newFrame, false);
separator = false;
}
}
myContainer.addStackFrames(Collections.emptyList(), true);
if (myAsyncStack != null) {
appendRelatedStack(myAsyncStack);
return;
}
List<StackFrameItem> relatedStack = null;
if (frame instanceof JavaStackFrame &&
AsyncStacksToggleAction.isAsyncStacksEnabled((XDebugSessionImpl)myDebugProcess.getXdebugProcess().getSession())) {
relatedStack = StackCapturingLineBreakpoint.getRelatedStack(frameProxy, suspendContext);
if (relatedStack != null) {
appendRelatedStack(relatedStack);
return;
}
else {
((JavaStackFrame)frame).setInsertCapturePoint(StackCapturingLineBreakpoint.getMatchingDisabledInsertionPoint(frameProxy));
}
// append agent stack after the next frame
relatedStack = StackCapturingLineBreakpoint.getAgentRelatedStack((JavaStackFrame)frame, suspendContext);
}
myDebugProcess.getManagerThread().schedule(
new AppendFrameCommand(suspendContext, myStackFramesIterator, myContainer, myAdded, mySkip));
new AppendFrameCommand(suspendContext, myStackFramesIterator, myContainer, myAdded, mySkip, relatedStack));
}
else {
myContainer.addStackFrames(Collections.<JavaStackFrame>emptyList(), true);
myContainer.addStackFrames(Collections.emptyList(), true);
}
}
void appendRelatedStack(@NotNull List<StackFrameItem> asyncStack) {
int i = 0;
boolean separator = true;
for (StackFrameItem stackFrame : asyncStack) {
if (i > StackCapturingLineBreakpoint.getMaxStackLength()) {
addFrameIfNeeded(new XStackFrame() {
@Override
public void customizePresentation(@NotNull ColoredTextContainer component) {
component.append("Too many frames, the rest is truncated...", SimpleTextAttributes.REGULAR_ITALIC_ATTRIBUTES);
}
}, true);
return;
}
i++;
if (stackFrame == null) {
separator = true;
continue;
}
StackFrameItem.CapturedStackFrame newFrame = stackFrame.createFrame(myDebugProcess);
if (showFrame(newFrame)) {
newFrame.setWithSeparator(separator);
addFrameIfNeeded(newFrame, false);
separator = false;
}
}
myContainer.addStackFrames(Collections.emptyList(), true);
}
}
private static boolean showFrame(@NotNull XStackFrame frame) {
@@ -12,8 +12,6 @@ import com.intellij.debugger.impl.DebuggerContextImpl;
import com.intellij.debugger.impl.DebuggerSession;
import com.intellij.debugger.impl.DebuggerUtilsEx;
import com.intellij.debugger.jdi.*;
import com.intellij.debugger.memory.utils.StackFrameItem;
import com.intellij.debugger.settings.CapturePoint;
import com.intellij.debugger.settings.DebuggerSettings;
import com.intellij.debugger.settings.NodeRendererSettings;
import com.intellij.debugger.ui.breakpoints.Breakpoint;
@@ -35,7 +33,6 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.ui.ColoredTextContainer;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.text.CharArrayUtil;
import com.intellij.xdebugger.XDebugSession;
@@ -68,7 +65,6 @@ public class JavaStackFrame extends XStackFrame implements JVMStackFrameInfoProv
@NotNull private final StackFrameDescriptorImpl myDescriptor;
private JavaDebuggerEvaluator myEvaluator = null;
private final String myEqualityObject;
private CapturePoint myInsertCapturePoint;
public JavaStackFrame(@NotNull StackFrameDescriptorImpl descriptor, boolean update) {
myDescriptor = descriptor;
@@ -116,9 +112,6 @@ public class JavaStackFrame extends XStackFrame implements JVMStackFrameInfoProv
}
}
JavaFramesListRenderer.customizePresentation(myDescriptor, component, selectedDescriptor);
if (myInsertCapturePoint != null) {
component.setIcon(XDebuggerUIConstants.INFORMATION_MESSAGE_ICON);
}
}
@Override
@@ -133,12 +126,6 @@ public class JavaStackFrame extends XStackFrame implements JVMStackFrameInfoProv
@Override
public void threadAction(@NotNull SuspendContextImpl suspendContext) {
if (node.isObsolete()) return;
if (myInsertCapturePoint != null) {
node.setMessage("Async stacktrace from " +
myInsertCapturePoint.myClassName + "." + myInsertCapturePoint.myMethodName +
" could be available here, enable in", XDebuggerUIConstants.INFORMATION_MESSAGE_ICON,
SimpleTextAttributes.REGULAR_ATTRIBUTES, StackFrameItem.CAPTURE_SETTINGS_OPENER);
}
XValueChildrenList children = new XValueChildrenList();
buildVariablesThreadAction(getFrameDebuggerContext(getDebuggerContext()), children, node);
node.addChildren(children, true);
@@ -691,10 +678,6 @@ public class JavaStackFrame extends XStackFrame implements JVMStackFrameInfoProv
return rangeRef.get();
}
public void setInsertCapturePoint(CapturePoint insertCapturePoint) {
myInsertCapturePoint = insertCapturePoint;
}
@Override
public boolean isSynthetic() {
return myDescriptor.isSynthetic();
@@ -10,7 +10,6 @@ import com.intellij.debugger.engine.jdi.StackFrameProxy;
import com.intellij.debugger.engine.requests.RequestManagerImpl;
import com.intellij.debugger.jdi.StackFrameProxyImpl;
import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
import com.intellij.debugger.settings.CaptureSettingsProvider;
import com.intellij.debugger.ui.breakpoints.Breakpoint;
import com.intellij.debugger.ui.breakpoints.BreakpointWithHighlighter;
import com.intellij.debugger.ui.breakpoints.LineBreakpoint;
@@ -208,7 +207,6 @@ public class DebuggerSession implements AbstractDebuggerSession {
myState = new DebuggerSessionState(State.STOPPED, null);
myDebugProcess.addDebugProcessListener(new MyDebugProcessListener(debugProcess));
myDebugProcess.addEvaluationListener(new MyEvaluationListener());
myDebugProcess.setAgentInsertPoints(CaptureSettingsProvider.getIdeInsertPoints());
ValueLookupManager.getInstance(getProject()).startListening();
mySearchScope = environment.getSearchScope();
myAlternativeJre = environment.getAlternativeJre();
@@ -1,6 +1,4 @@
/*
* Copyright 2000-2017 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.
*/
// 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.utils;
import com.intellij.debugger.DebuggerBundle;
@@ -146,7 +144,7 @@ public class StackFrameItem {
StackFrameItem frameItem = new StackFrameItem(location, vars);
res.add(frameItem);
List<StackFrameItem> relatedStack = StackCapturingLineBreakpoint.getRelatedStack(frame, suspendContext, false);
List<StackFrameItem> relatedStack = StackCapturingLineBreakpoint.getRelatedStack(frame, suspendContext);
if (!ContainerUtil.isEmpty(relatedStack)) {
res.add(null); // separator
res.addAll(relatedStack);
@@ -3,17 +3,14 @@ package com.intellij.debugger.settings;
import com.intellij.debugger.engine.JVMNameUtil;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.jdi.DecompiledLocalVariable;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.util.containers.ContainerUtil;
import one.util.streamex.StreamEx;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
@@ -24,7 +21,6 @@ public class CaptureSettingsProvider {
private static final List<AgentCapturePoint> CAPTURE_POINTS = new ArrayList<>();
private static final List<AgentInsertPoint> INSERT_POINTS = new ArrayList<>();
private static final List<CapturePoint> IDE_INSERT_POINTS;
private static final KeyProvider THIS_KEY = new StringKeyProvider("this");
private static final KeyProvider FIRST_PARAM = param(0);
@@ -74,8 +70,6 @@ public class CaptureSettingsProvider {
addCapture("com/sun/glass/ui/InvokeLaterDispatcher", "invokeLater", FIRST_PARAM);
addInsert("com/sun/glass/ui/InvokeLaterDispatcher$Future", "run",
new FieldKeyProvider("com/sun/glass/ui/InvokeLaterDispatcher$Future", "runnable"));
IDE_INSERT_POINTS = StreamEx.of(INSERT_POINTS).map(p -> p.myInsertPoint).nonNull().toList();
}
public static List<AgentPoint> getPoints() {
@@ -86,15 +80,6 @@ public class CaptureSettingsProvider {
return res;
}
public static List<CapturePoint> getIdeInsertPoints() {
List<CapturePoint> res = Collections.unmodifiableList(IDE_INSERT_POINTS);
if (Registry.is("debugger.capture.points.agent.annotations")) {
res = ContainerUtil.concat(
res, StreamEx.of(getAnnotationPoints()).select(AgentInsertPoint.class).map(p -> p.myInsertPoint).nonNull().toList());
}
return res;
}
private static List<AgentPoint> getAnnotationPoints() {
return ReadAction.compute(() -> {
List<AgentPoint> annotationPoints = new ArrayList<>();
@@ -178,25 +163,8 @@ public class CaptureSettingsProvider {
}
public static class AgentInsertPoint extends AgentPoint {
public final CapturePoint myInsertPoint; // for IDE
public AgentInsertPoint(String className, String methodName, String methodDesc, KeyProvider key) {
super(className, methodName, methodDesc, key);
this.myInsertPoint = new CapturePoint();
myInsertPoint.myInsertClassName = className.replaceAll("/", ".");
myInsertPoint.myInsertMethodName = methodName;
if (myKey instanceof FieldKeyProvider) {
myInsertPoint.myInsertKeyExpression = ((FieldKeyProvider)myKey).myFieldName;
}
else {
String keyStr = key.asString();
try {
myInsertPoint.myInsertKeyExpression = DecompiledLocalVariable.PARAM_PREFIX + Integer.parseInt(keyStr);
}
catch (NumberFormatException ignored) {
myInsertPoint.myInsertKeyExpression = keyStr;
}
}
}
@Override
@@ -10,6 +10,7 @@ import com.intellij.debugger.engine.evaluation.expression.EvaluatorBuilderImpl;
import com.intellij.debugger.engine.evaluation.expression.ExpressionEvaluator;
import com.intellij.debugger.engine.evaluation.expression.ExpressionEvaluatorImpl;
import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
import com.intellij.debugger.impl.DebuggerUtilsEx;
import com.intellij.debugger.jdi.*;
import com.intellij.debugger.memory.utils.StackFrameItem;
import com.intellij.debugger.settings.CapturePoint;
@@ -24,6 +25,7 @@ import com.intellij.openapi.util.ThrowableComputable;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
import com.intellij.rt.debugger.agent.CaptureStorage;
import com.intellij.ui.SimpleColoredComponent;
import com.intellij.util.containers.ContainerUtil;
import com.sun.jdi.*;
@@ -143,11 +145,7 @@ public class StackCapturingLineBreakpoint extends WildcardMethodBreakpoint {
public static void createAll(DebugProcessImpl debugProcess) {
DebuggerManagerThreadImpl.assertIsManagerThread();
StreamEx<CapturePoint> points = StreamEx.of(DebuggerSettings.getInstance().getCapturePoints()).filter(c -> c.myEnabled);
if (isAgentEnabled()) {
points = points.append(debugProcess.getAgentInsertPoints());
}
points.forEach(c -> track(debugProcess, c));
DebuggerSettings.getInstance().getCapturePoints().stream().filter(c -> c.myEnabled).forEach(c -> track(debugProcess, c));
}
public static void clearCaches(DebugProcessImpl debugProcess) {
@@ -228,12 +226,29 @@ public class StackCapturingLineBreakpoint extends WildcardMethodBreakpoint {
}
@Nullable
public static List<StackFrameItem> getRelatedStack(@NotNull StackFrameProxyImpl frame,
@NotNull SuspendContextImpl suspendContext,
boolean checkInProcessData) {
public static List<StackFrameItem> getAgentRelatedStack(JavaStackFrame frame, @NotNull SuspendContextImpl suspendContext) {
if (isAgentEnabled()) {
Location location = frame.getDescriptor().getLocation();
if (location != null) {
Method method = DebuggerUtilsEx.getMethod(location);
if (method != null && method.name().endsWith(CaptureStorage.GENERATED_INSERT_METHOD_POSTFIX)) {
try {
return getProcessCapturedStack(new EvaluationContextImpl(suspendContext, frame.getStackFrameProxy()));
}
catch (EvaluateException e) {
LOG.error(e);
}
}
}
}
return null;
}
@Nullable
public static List<StackFrameItem> getRelatedStack(@NotNull StackFrameProxyImpl frame, @NotNull SuspendContextImpl suspendContext) {
DebugProcessImpl debugProcess = suspendContext.getDebugProcess();
Map<Object, List<StackFrameItem>> capturedStacks = debugProcess.getUserData(CAPTURED_STACKS);
if (ContainerUtil.isEmpty(capturedStacks) && !isAgentEnabled()) {
if (ContainerUtil.isEmpty(capturedStacks)) {
return null;
}
List<StackCapturingLineBreakpoint> captureBreakpoints = debugProcess.getUserData(CAPTURE_BREAKPOINTS);
@@ -250,18 +265,8 @@ public class StackCapturingLineBreakpoint extends WildcardMethodBreakpoint {
if ((StringUtil.isEmpty(insertClassName) || StringUtil.equals(insertClassName, className)) &&
StringUtil.equals(b.myCapturePoint.myInsertMethodName, methodName)) {
try {
EvaluationContextImpl evaluationContext = new EvaluationContextImpl(suspendContext, frame);
Value key = b.myInsertEvaluator.evaluate(evaluationContext);
List<StackFrameItem> items = null;
if (key instanceof ObjectReference) {
if (capturedStacks != null) {
items = capturedStacks.get(getKey((ObjectReference)key));
}
if (items == null && checkInProcessData) {
items = getProcessCapturedStack(key, evaluationContext);
}
}
return items;
Value key = b.myInsertEvaluator.evaluate(new EvaluationContextImpl(suspendContext, frame));
return key instanceof ObjectReference ? capturedStacks.get(getKey((ObjectReference)key)) : null;
}
catch (EvaluateException e) {
LOG.debug(e);
@@ -281,7 +286,7 @@ public class StackCapturingLineBreakpoint extends WildcardMethodBreakpoint {
private static final Key<Pair<ClassType, Method>> CAPTURE_STORAGE_METHOD = Key.create("CAPTURE_STORAGE_METHOD");
public static final Pair<ClassType, Method> NO_CAPTURE_AGENT = Pair.empty();
private static List<StackFrameItem> getProcessCapturedStack(Value key, EvaluationContextImpl evalContext)
private static List<StackFrameItem> getProcessCapturedStack(EvaluationContextImpl evalContext)
throws EvaluateException {
EvaluationContextImpl evaluationContext = evalContext.withAutoLoadClasses(false);
@@ -290,13 +295,13 @@ public class StackCapturingLineBreakpoint extends WildcardMethodBreakpoint {
if (methodPair == null) {
try {
ClassType captureClass = (ClassType)process.findClass(evaluationContext, "com.intellij.rt.debugger.agent.CaptureStorage", null);
ClassType captureClass = (ClassType)process.findClass(evaluationContext, CaptureStorage.class.getName(), null);
if (captureClass == null) {
methodPair = NO_CAPTURE_AGENT;
LOG.debug("Error loading debug agent", "agent class not found");
}
else {
methodPair = Pair.create(captureClass, captureClass.methodsByName("getRelatedStack").get(0));
methodPair = Pair.create(captureClass, captureClass.methodsByName("getCurrentCapturedStack").get(0));
}
}
catch (EvaluateException e) {
@@ -311,7 +316,7 @@ public class StackCapturingLineBreakpoint extends WildcardMethodBreakpoint {
}
VirtualMachineProxyImpl virtualMachineProxy = process.getVirtualMachineProxy();
List<Value> args = Arrays.asList(key, virtualMachineProxy.mirrorOf(getMaxStackLength()));
List<Value> args = Collections.singletonList(virtualMachineProxy.mirrorOf(getMaxStackLength()));
Pair<ClassType, Method> finalMethodPair = methodPair;
Value resArray = evaluationContext.computeAndKeep(
() -> process.invokeMethod(evaluationContext, finalMethodPair.first, finalMethodPair.second,