mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
async debugger: capture locals values, show captured stack inside the frames view
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,14 +22,18 @@ import com.intellij.debugger.impl.DebuggerUtilsEx;
|
||||
import com.intellij.debugger.jdi.StackFrameProxyImpl;
|
||||
import com.intellij.debugger.jdi.ThreadGroupReferenceProxyImpl;
|
||||
import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
|
||||
import com.intellij.debugger.memory.utils.StackFrameItem;
|
||||
import com.intellij.debugger.ui.breakpoints.StackCapturingLineBreakpoint;
|
||||
import com.intellij.debugger.ui.impl.watch.MethodsTracker;
|
||||
import com.intellij.debugger.ui.impl.watch.StackFrameDescriptorImpl;
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.xdebugger.frame.XExecutionStack;
|
||||
import com.intellij.xdebugger.frame.XStackFrame;
|
||||
import com.intellij.xdebugger.settings.XDebuggerSettingsManager;
|
||||
import com.sun.jdi.Location;
|
||||
import com.sun.jdi.ObjectReference;
|
||||
import com.sun.jdi.ThreadReference;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -37,6 +41,8 @@ import org.jetbrains.annotations.Nullable;
|
||||
import javax.swing.*;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author egor
|
||||
@@ -184,7 +190,16 @@ public class JavaExecutionStack extends XExecutionStack {
|
||||
myStackFramesIterator.next();
|
||||
}
|
||||
else {
|
||||
frame = createStackFrame(myStackFramesIterator.next(), myTracker);
|
||||
StackFrameProxyImpl frameProxy = myStackFramesIterator.next();
|
||||
|
||||
// replace the rest with the related stack
|
||||
List<? extends XStackFrame> relatedStack = getRelatedStack(frameProxy, myDebugProcess);
|
||||
if (!ContainerUtil.isEmpty(relatedStack)) {
|
||||
myContainer.addStackFrames(relatedStack, true);
|
||||
return;
|
||||
}
|
||||
|
||||
frame = createStackFrame(frameProxy, myTracker);
|
||||
if (first && !myTopFrameReady) {
|
||||
myTopFrame = frame;
|
||||
myTopFrameReady = true;
|
||||
@@ -204,6 +219,22 @@ public class JavaExecutionStack extends XExecutionStack {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static List<? extends XStackFrame> getRelatedStack(StackFrameProxyImpl frame, DebugProcessImpl process) {
|
||||
Map<ObjectReference, List<StackFrameItem>> data = process.getUserData(StackCapturingLineBreakpoint.CAPTURED_STACKS);
|
||||
if (data != null) {
|
||||
try {
|
||||
ObjectReference thisObject = frame.thisObject();
|
||||
if (thisObject != null) {
|
||||
return data.get(thisObject);
|
||||
}
|
||||
}
|
||||
catch (EvaluateException ignore) {
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean showFrame(@NotNull XStackFrame frame) {
|
||||
if (XDebuggerSettingsManager.getInstance().getDataViewSettings().isShowLibraryStackFrames()) return true;
|
||||
if (frame instanceof JavaStackFrame) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -411,7 +411,7 @@ public class PositionManagerImpl implements PositionManager, MultiRequestPositio
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiClass findClass(Project project, String originalQName, GlobalSearchScope searchScope) {
|
||||
public static PsiClass findClass(Project project, String originalQName, GlobalSearchScope searchScope) {
|
||||
PsiClass psiClass = DebuggerUtils.findClass(originalQName, project, searchScope); // try to lookup original name first
|
||||
if (psiClass == null) {
|
||||
int dollar = originalQName.indexOf('$');
|
||||
@@ -684,7 +684,7 @@ public class PositionManagerImpl implements PositionManager, MultiRequestPositio
|
||||
}
|
||||
}
|
||||
|
||||
private static class ClsSourcePosition extends RemappedSourcePosition {
|
||||
public static class ClsSourcePosition extends RemappedSourcePosition {
|
||||
private final int myOriginalLine;
|
||||
|
||||
public ClsSourcePosition(SourcePosition delegate, int originalLine) {
|
||||
|
||||
+3
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -243,7 +243,8 @@ public class ConstructorInstancesTracker implements TrackerForNewInstances, Disp
|
||||
if (myReference.equals(thisRef.referenceType()) && data != null) {
|
||||
thisRef.disableCollection();
|
||||
myTrackedObjects.add(thisRef);
|
||||
final List<StackFrameItem> frame = StackFrameItem.createFrames(suspendContext.getThread());
|
||||
final List<StackFrameItem> frame =
|
||||
StackFrameItem.createFrames(suspendContext.getThread(), suspendContext.getDebugProcess(), false);
|
||||
data.getTrackedStacks().addStack(thisRef, frame);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,26 +15,79 @@
|
||||
*/
|
||||
package com.intellij.debugger.memory.utils;
|
||||
|
||||
import com.intellij.debugger.SourcePosition;
|
||||
import com.intellij.debugger.engine.DebugProcessImpl;
|
||||
import com.intellij.debugger.engine.PositionManagerImpl;
|
||||
import com.intellij.debugger.engine.evaluation.EvaluateException;
|
||||
import com.intellij.debugger.jdi.StackFrameProxyImpl;
|
||||
import com.intellij.debugger.impl.DebuggerUtilsEx;
|
||||
import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
|
||||
import com.intellij.debugger.settings.NodeRendererSettings;
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.NullableLazyValue;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.PsiCompiledFile;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.ui.ColoredTextContainer;
|
||||
import com.intellij.ui.SimpleTextAttributes;
|
||||
import com.intellij.util.PlatformIcons;
|
||||
import com.intellij.util.ui.EmptyIcon;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import com.intellij.xdebugger.XSourcePosition;
|
||||
import com.intellij.xdebugger.frame.*;
|
||||
import com.sun.jdi.Location;
|
||||
import com.sun.jdi.ObjectReference;
|
||||
import com.sun.jdi.PrimitiveValue;
|
||||
import com.sun.jdi.Value;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class StackFrameItem {
|
||||
public class StackFrameItem extends XStackFrame {
|
||||
private static final Logger LOG = Logger.getInstance(StackFrameItem.class);
|
||||
|
||||
private final Project myProject;
|
||||
private final GlobalSearchScope mySearchScope;
|
||||
|
||||
private final String myFilePath;
|
||||
private final String myMethodName;
|
||||
private final int myLineNumber;
|
||||
private List<VariableItem> myVariables = null;
|
||||
|
||||
public StackFrameItem(@NotNull String path, @NotNull String methodName, int line) {
|
||||
private final NullableLazyValue<XSourcePosition> mySourcePosition;
|
||||
|
||||
public StackFrameItem(Project project,
|
||||
GlobalSearchScope scope,
|
||||
@NotNull String path,
|
||||
@NotNull String methodName,
|
||||
int line) {
|
||||
myProject = project;
|
||||
mySearchScope = scope;
|
||||
myFilePath = path.replace('\\', '.');
|
||||
myMethodName = methodName;
|
||||
myLineNumber = line;
|
||||
|
||||
//TODO: need to reuse PositionManager somehow
|
||||
mySourcePosition = NullableLazyValue.createValue(() -> {
|
||||
PsiClass psiClass = PositionManagerImpl.findClass(myProject, myFilePath, mySearchScope);
|
||||
if (psiClass == null) {
|
||||
return null;
|
||||
}
|
||||
SourcePosition position = SourcePosition.createFromLine(psiClass.getContainingFile(), myLineNumber - 1);
|
||||
PsiFile psiFile = psiClass.getContainingFile().getOriginalFile();
|
||||
if (psiFile instanceof PsiCompiledFile) {
|
||||
position = new PositionManagerImpl.ClsSourcePosition(position, myLineNumber - 1);
|
||||
}
|
||||
return DebuggerUtilsEx.toXSourcePosition(position);
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -61,20 +114,138 @@ public class StackFrameItem {
|
||||
return myLineNumber;
|
||||
}
|
||||
|
||||
public static List<StackFrameItem> createFrames(ThreadReferenceProxyImpl threadReferenceProxy) throws EvaluateException {
|
||||
List<StackFrameProxyImpl> stack = threadReferenceProxy == null ? null : threadReferenceProxy.frames();
|
||||
@Override
|
||||
public void customizePresentation(@NotNull ColoredTextContainer component) {
|
||||
component.setIcon(JBUI.scale(EmptyIcon.create(6)));
|
||||
component.append(String.format("%s:%d, %s", myMethodName, myLineNumber, myFilePath), SimpleTextAttributes.REGULAR_ATTRIBUTES);
|
||||
}
|
||||
|
||||
if (stack != null) {
|
||||
return StreamEx.of(stack).map(frame -> {
|
||||
@Nullable
|
||||
@Override
|
||||
public XSourcePosition getSourcePosition() {
|
||||
return mySourcePosition.getValue();
|
||||
}
|
||||
|
||||
void addVariable(VariableItem var) {
|
||||
if (myVariables == null) {
|
||||
myVariables = new ArrayList<>();
|
||||
}
|
||||
myVariables.add(var);
|
||||
}
|
||||
|
||||
public static List<StackFrameItem> createFrames(@Nullable ThreadReferenceProxyImpl threadReferenceProxy,
|
||||
DebugProcessImpl process,
|
||||
boolean withVars)
|
||||
throws EvaluateException {
|
||||
if (threadReferenceProxy != null) {
|
||||
return StreamEx.of(threadReferenceProxy.frames()).map(frame -> {
|
||||
try {
|
||||
Location loc = frame.location();
|
||||
return new StackFrameItem(loc.declaringType().name(), loc.method().name(), loc.lineNumber());
|
||||
StackFrameItem frameItem = new StackFrameItem(process.getProject(),
|
||||
process.getSearchScope(),
|
||||
loc.declaringType().name(),
|
||||
loc.method().name(),
|
||||
loc.lineNumber());
|
||||
if (withVars) {
|
||||
try {
|
||||
ObjectReference thisObject = frame.thisObject();
|
||||
if (thisObject != null) {
|
||||
frameItem.addVariable(createVariable(thisObject, "this", VariableItem.VarType.OBJECT));
|
||||
}
|
||||
}
|
||||
catch (EvaluateException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
|
||||
try {
|
||||
frame.visibleVariables().forEach(v -> {
|
||||
try {
|
||||
Value value = frame.getValue(v);
|
||||
VariableItem.VarType varType = VariableItem.VarType.OBJECT;
|
||||
if (v.getVariable().isArgument()) {
|
||||
varType = VariableItem.VarType.PARAM;
|
||||
}
|
||||
else if (value instanceof PrimitiveValue) {
|
||||
varType = VariableItem.VarType.PRIMITIVE;
|
||||
}
|
||||
frameItem.addVariable(createVariable(value, v.name(), varType));
|
||||
}
|
||||
catch (EvaluateException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (EvaluateException ignore) {
|
||||
}
|
||||
}
|
||||
return frameItem;
|
||||
}
|
||||
catch (EvaluateException e) {
|
||||
LOG.error(e);
|
||||
return null;
|
||||
}
|
||||
}).nonNull().toList();
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private static VariableItem createVariable(Value value, String name, VariableItem.VarType varType) {
|
||||
String type = null;
|
||||
String valueText = "null";
|
||||
if (value instanceof ObjectReference) {
|
||||
valueText = "";
|
||||
type = value.type().name() + "@" + ((ObjectReference)value).uniqueID();
|
||||
}
|
||||
else if (value != null) {
|
||||
valueText = value.toString();
|
||||
type = value.type().name();
|
||||
}
|
||||
return new VariableItem(name, type, valueText, varType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void computeChildren(@NotNull XCompositeNode node) {
|
||||
if (myVariables != null) {
|
||||
XValueChildrenList children = new XValueChildrenList();
|
||||
myVariables.forEach(v -> children.add(v.myName, new XValue() {
|
||||
@Override
|
||||
public void computePresentation(@NotNull XValueNode node, @NotNull XValuePlace place) {
|
||||
String type = NodeRendererSettings.getInstance().getClassRenderer().renderTypeName(v.myType);
|
||||
Icon icon;
|
||||
switch (v.myVarType) {
|
||||
case PARAM:
|
||||
icon = PlatformIcons.PARAMETER_ICON;
|
||||
break;
|
||||
case PRIMITIVE:
|
||||
icon = AllIcons.Debugger.Db_primitive;
|
||||
type = null;
|
||||
break;
|
||||
default:
|
||||
icon = AllIcons.Debugger.Value;
|
||||
}
|
||||
node.setPresentation(icon, type, v.myValue, false);
|
||||
}
|
||||
}));
|
||||
node.addChildren(children, true);
|
||||
}
|
||||
else {
|
||||
node.addChildren(XValueChildrenList.EMPTY, true);
|
||||
}
|
||||
}
|
||||
|
||||
private static class VariableItem {
|
||||
enum VarType {PARAM, PRIMITIVE, OBJECT}
|
||||
|
||||
private final String myName;
|
||||
private final String myType;
|
||||
private final String myValue;
|
||||
private final VarType myVarType;
|
||||
|
||||
public VariableItem(String name, String type, String value, VarType varType) {
|
||||
myName = name;
|
||||
myType = type;
|
||||
myValue = value;
|
||||
myVarType = varType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -97,7 +97,7 @@ public class StackCapturingLineBreakpoint extends WildcardMethodBreakpoint {
|
||||
}
|
||||
Value key = ContainerUtil.getOrElse(frameProxy.getArgumentValues(), myParamNo, null);
|
||||
if (key instanceof ObjectReference) {
|
||||
stacks.put((ObjectReference)key, StackFrameItem.createFrames(suspendContext.getThread()));
|
||||
stacks.put((ObjectReference)key, StackFrameItem.createFrames(suspendContext.getThread(), process, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -76,8 +76,9 @@ public class ClassRenderer extends NodeRendererImpl{
|
||||
myProperties.setEnabled(true);
|
||||
}
|
||||
|
||||
public final String renderTypeName(final String typeName) {
|
||||
if (SHOW_FQ_TYPE_NAMES) {
|
||||
@Nullable
|
||||
public final String renderTypeName(@Nullable final String typeName) {
|
||||
if (SHOW_FQ_TYPE_NAMES || typeName == null) {
|
||||
return typeName;
|
||||
}
|
||||
String baseLambdaClassName = DebuggerUtilsEx.getLambdaBaseClassName(typeName);
|
||||
|
||||
Reference in New Issue
Block a user