async debugger:

- store Locations for frames
- reuse XDebuggerFramesList in views
This commit is contained in:
Egor.Ushakov
2017-01-30 20:35:05 +03:00
parent a96b45ab36
commit ba32cfcbcf
12 changed files with 265 additions and 308 deletions
@@ -39,14 +39,12 @@ public class ShowRelatedStackAction extends AnAction {
Project project = e.getProject();
List<StackFrameItem> stack = getRelatedStack(e);
if (project != null && stack != null) {
DebuggerContextImpl debuggerContext = DebuggerAction.getDebuggerContext(e.getDataContext());
DebugProcessImpl debugProcess = debuggerContext.getDebugProcess();
DebugProcessImpl debugProcess = DebuggerAction.getDebuggerContext(e.getDataContext()).getDebugProcess();
if (debugProcess == null) {
return;
}
new StackFramePopup(project, stack, debugProcess.getSearchScope()).show();
StackFramePopup.show(stack, debugProcess);
}
}
@@ -0,0 +1,24 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.debugger.engine;
/**
* @author egor
*/
public interface JVMStackFrameInfoProvider {
boolean isSynthetic();
boolean isInLibraryContent();
}
@@ -22,12 +22,15 @@ 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.openapi.util.registry.Registry;
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;
@@ -204,9 +207,26 @@ public class JavaExecutionStack extends XExecutionStack {
// replace the rest with the related stack (if available)
if (Registry.is("debugger.capture.points") && frame instanceof JavaStackFrame) {
List<? extends XStackFrame> relatedStack = StackCapturingLineBreakpoint.getRelatedStack(frameProxy, suspendContext);
List<StackFrameItem> relatedStack = StackCapturingLineBreakpoint.getRelatedStack(frameProxy, suspendContext);
if (!ContainerUtil.isEmpty(relatedStack)) {
myContainer.addStackFrames(relatedStack, true);
int i = 0;
for (StackFrameItem stackFrame : relatedStack) {
if (i > StackCapturingLineBreakpoint.MAX_STACK_LENGTH) {
myContainer.addStackFrames(Collections.singletonList(new XStackFrame() {
@Override
public void customizePresentation(@NotNull ColoredTextContainer component) {
component.append("Too many frames, the rest is truncated...", SimpleTextAttributes.REGULAR_ITALIC_ATTRIBUTES);
}
}), true);
return;
}
XStackFrame newFrame = stackFrame.createFrame(myDebugProcess);
if (showFrame(newFrame)) {
myContainer.addStackFrames(Collections.singletonList(newFrame), false);
}
i++;
}
myContainer.addStackFrames(Collections.emptyList(), true);
return;
}
}
@@ -221,10 +241,10 @@ public class JavaExecutionStack extends XExecutionStack {
}
private static boolean showFrame(@NotNull XStackFrame frame) {
if (XDebuggerSettingsManager.getInstance().getDataViewSettings().isShowLibraryStackFrames()) return true;
if (frame instanceof JavaStackFrame) {
StackFrameDescriptorImpl descriptor = ((JavaStackFrame)frame).getDescriptor();
return !descriptor.isSynthetic() && !descriptor.isInLibraryContent();
if (!XDebuggerSettingsManager.getInstance().getDataViewSettings().isShowLibraryStackFrames() &&
frame instanceof JVMStackFrameInfoProvider) {
JVMStackFrameInfoProvider info = (JVMStackFrameInfoProvider)frame;
return !info.isSynthetic() && !info.isInLibraryContent();
}
return true;
}
@@ -66,7 +66,7 @@ import java.util.*;
/**
* @author egor
*/
public class JavaStackFrame extends XStackFrame {
public class JavaStackFrame extends XStackFrame implements JVMStackFrameInfoProvider {
private static final Logger LOG = Logger.getInstance(JavaStackFrame.class);
private final DebugProcessImpl myDebugProcess;
@@ -668,4 +668,14 @@ public class JavaStackFrame extends XStackFrame {
});
return rangeRef.get();
}
@Override
public boolean isSynthetic() {
return myDescriptor.isSynthetic();
}
@Override
public boolean isInLibraryContent() {
return myDescriptor.isInLibraryContent();
}
}
@@ -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.
@@ -16,13 +16,13 @@
package com.intellij.debugger.memory.action.tracking;
import com.intellij.debugger.DebuggerManager;
import com.intellij.debugger.engine.DebugProcessImpl;
import com.intellij.debugger.memory.action.DebuggerTreeAction;
import com.intellij.debugger.memory.component.MemoryViewDebugProcessData;
import com.intellij.debugger.memory.ui.StackFramePopup;
import com.intellij.debugger.memory.utils.StackFrameItem;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.project.Project;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.xdebugger.XDebugSession;
import com.intellij.xdebugger.XDebuggerManager;
import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl;
@@ -45,9 +45,9 @@ public class JumpToAllocationSourceAction extends DebuggerTreeAction {
if (project != null && stack != null) {
final XDebugSession session = XDebuggerManager.getInstance(project).getCurrentSession();
if (session != null) {
final GlobalSearchScope searchScope = DebuggerManager.getInstance(project)
.getDebugProcess(session.getDebugProcess().getProcessHandler()).getSearchScope();
new StackFramePopup(project, stack, searchScope).show();
DebugProcessImpl process = (DebugProcessImpl)DebuggerManager.getInstance(project)
.getDebugProcess(session.getDebugProcess().getProcessHandler());
StackFramePopup.show(stack, process);
}
}
}
@@ -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.
@@ -39,7 +39,6 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.wm.IdeFocusManager;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.ui.DoubleClickListener;
import com.intellij.ui.JBColor;
import com.intellij.ui.components.JBLabel;
@@ -221,9 +220,7 @@ public class InstancesWindow extends DialogWrapper {
});
final StackFrameList list = new StackFrameList(myProject,
Collections.emptyList(),
GlobalSearchScope.allScope(myProject));
final StackFrameList list = new StackFrameList(myDebugProcess);
list.addListSelectionListener(e -> list.navigateToSelectedValue(false));
new DoubleClickListener() {
@@ -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.
@@ -42,7 +42,6 @@ class InstancesWithStackFrameView {
private static final String EMPTY_TEXT_WHEN_ITEM_NOT_SELECTED = "Select instance to see stack frame";
private static final String EMPTY_TEXT_WHEN_STACK_NOT_FOUND = "No stack frame for this instance";
private static final String TEXT_FOR_ARRAYS = "Arrays could not be tracked";
private static final List<StackFrameItem> EMPTY_FRAME = Collections.emptyList();
private float myHidedProportion;
@@ -108,7 +107,7 @@ class InstancesWithStackFrameView {
if (ref != null && data != null) {
List<StackFrameItem> stack = data.getTrackedStacks().getStack(ref);
if (stack != null) {
list.setFrame(stack);
list.setFrameItems(stack);
if (mySplitter.getProportion() == 1.f) {
mySplitter.setProportion(DEFAULT_SPLITTER_PROPORTION);
}
@@ -120,7 +119,7 @@ class InstancesWithStackFrameView {
list.setEmptyText(EMPTY_TEXT_WHEN_ITEM_NOT_SELECTED);
}
list.setFrame(EMPTY_FRAME);
list.setFrameItems(Collections.emptyList());
});
}
@@ -15,10 +15,10 @@
*/
package com.intellij.debugger.memory.ui;
import com.intellij.debugger.engine.DebuggerUtils;
import com.intellij.debugger.engine.DebugProcessImpl;
import com.intellij.debugger.engine.events.DebuggerCommandImpl;
import com.intellij.debugger.memory.utils.StackFrameItem;
import com.intellij.execution.filters.OpenFileHyperlinkInfo;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.ScrollType;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.fileEditor.FileEditorManager;
@@ -26,125 +26,97 @@ import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.ui.ColoredListCellRenderer;
import com.intellij.ui.components.JBList;
import com.intellij.xdebugger.XSourcePosition;
import com.intellij.xdebugger.frame.XStackFrame;
import com.intellij.xdebugger.impl.frame.XDebuggerFramesList;
import com.intellij.xdebugger.impl.ui.DebuggerUIUtil;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.util.ArrayList;
import java.util.List;
class StackFrameList extends JBList<StackFrameItem> {
private static final char ANONYMOUS_CLASS_DELIMITER = '$';
class StackFrameList extends XDebuggerFramesList {
private static final MyOpenFilesState myEditorState = new MyOpenFilesState();
private List<StackFrameItem> myStackFrames;
private final Project myProject;
private final GlobalSearchScope myScope;
private final MyListModel myModel = new MyListModel();
private final DebugProcessImpl myDebugProcess;
StackFrameList(@NotNull Project project,
@NotNull List<StackFrameItem> stack,
@NotNull GlobalSearchScope searchScope) {
super();
myStackFrames = new ArrayList<>(stack);
myProject = project;
myScope = searchScope;
setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
setModel(myModel);
setCellRenderer(new ColoredListCellRenderer<StackFrameItem>() {
@Override
protected void customizeCellRenderer(@NotNull JList<? extends StackFrameItem> list,
StackFrameItem value, int index, boolean isSelected, boolean hasFocus) {
value.customizePresentation(this);
setIcon(null); // no icons in the list
}
});
StackFrameList(DebugProcessImpl debugProcess) {
super(debugProcess.getProject());
myDebugProcess = debugProcess;
}
void setFrame(@NotNull List<StackFrameItem> stack) {
myModel.update(stack);
void setFrameItems(@NotNull List<StackFrameItem> items) {
setFrameItems(items, null);
}
void navigateToSelectedValue(boolean focusOnEditor) {
StackFrameItem selectedValue = getSelectedValue();
if (selectedValue != null) {
navigateToFrame(selectedValue, focusOnEditor);
}
}
private void navigateToFrame(@NotNull StackFrameItem frame, boolean focusOnEditor) {
String path = frame.path();
int anonymousClassDelimiterIndex = path.indexOf(ANONYMOUS_CLASS_DELIMITER);
int pathLength = anonymousClassDelimiterIndex > 0 ? anonymousClassDelimiterIndex : path.length();
path = path.substring(0, pathLength);
PsiClass psiClass = DebuggerUtils.findClass(path, myProject, myScope);
if (psiClass != null) {
ApplicationManager.getApplication().runReadAction(() -> {
PsiElement navigationElement = psiClass.getNavigationElement();
VirtualFile file = PsiUtilCore.getVirtualFile(navigationElement);
if (file == null) {
file = psiClass.getContainingFile().getVirtualFile();
}
OpenFileHyperlinkInfo info =
new OpenFileHyperlinkInfo(myProject, file, frame.line() - 1);
OpenFileDescriptor descriptor = info.getDescriptor();
if (descriptor != null) {
FileEditorManagerImpl manager = (FileEditorManagerImpl)FileEditorManager.getInstance(myProject);
VirtualFile lastFile = myEditorState.myLastOpenedFile;
if (myEditorState.myIsNeedToCloseLastOpenedFile && lastFile != null &&
manager.isFileOpen(lastFile) && !lastFile.equals(descriptor.getFile())) {
manager.closeFile(myEditorState.myLastOpenedFile, false, true);
void setFrameItems(@NotNull List<StackFrameItem> items, Runnable onDone) {
clear();
if (!items.isEmpty()) {
myDebugProcess.getManagerThread().schedule(new DebuggerCommandImpl() {
@Override
protected void action() throws Exception {
for (StackFrameItem frameInfo : items) {
StackFrameItem.CapturedStackFrame frame = frameInfo.createFrame(myDebugProcess);
DebuggerUIUtil.invokeLater(() -> getModel().addElement(frame));
}
descriptor.setScrollType(ScrollType.CENTER);
descriptor.setUseCurrentWindow(true);
if (lastFile == null || !lastFile.equals(descriptor.getFile())) {
myEditorState.myIsNeedToCloseLastOpenedFile = !manager.isFileOpen(descriptor.getFile());
}
descriptor.navigateInEditor(myProject, focusOnEditor);
FileEditor[] editors = manager.getEditors(descriptor.getFile());
if (editors.length != 0) {
myEditorState.myLastOpenedFile = descriptor.getFile();
if (onDone != null) {
onDone.run();
}
}
});
}
}
@Override
protected void onFrameChanged(Object selectedValue) {
navigateTo(selectedValue, false);
}
void navigateToSelectedValue(boolean focusOnEditor) {
navigateTo(getSelectedValue(), focusOnEditor);
}
private void navigateTo(Object frame, boolean focusOnEditor) {
if (frame instanceof XStackFrame) {
navigateToFrame((XStackFrame)frame, focusOnEditor);
}
}
private void navigateToFrame(@NotNull XStackFrame frame, boolean focusOnEditor) {
XSourcePosition position = frame.getSourcePosition();
if (position == null) return;
VirtualFile file = position.getFile();
int line = position.getLine();
Project project = myDebugProcess.getProject();
OpenFileHyperlinkInfo info = new OpenFileHyperlinkInfo(project, file, line);
OpenFileDescriptor descriptor = info.getDescriptor();
if (descriptor != null) {
FileEditorManagerImpl manager = (FileEditorManagerImpl)FileEditorManager.getInstance(project);
VirtualFile lastFile = myEditorState.myLastOpenedFile;
if (myEditorState.myIsNeedToCloseLastOpenedFile && lastFile != null &&
manager.isFileOpen(lastFile) && !lastFile.equals(descriptor.getFile())) {
manager.closeFile(myEditorState.myLastOpenedFile, false, true);
}
descriptor.setScrollType(ScrollType.CENTER);
descriptor.setUseCurrentWindow(true);
if (lastFile == null || !lastFile.equals(descriptor.getFile())) {
myEditorState.myIsNeedToCloseLastOpenedFile = !manager.isFileOpen(descriptor.getFile());
}
descriptor.navigateInEditor(project, focusOnEditor);
FileEditor[] editors = manager.getEditors(descriptor.getFile());
if (editors.length != 0) {
myEditorState.myLastOpenedFile = descriptor.getFile();
}
}
}
private static class MyOpenFilesState {
VirtualFile myLastOpenedFile;
boolean myIsNeedToCloseLastOpenedFile;
}
private class MyListModel extends AbstractListModel<StackFrameItem> {
void update(@NotNull List<StackFrameItem> newFrame) {
fireIntervalRemoved(this, 0, getSize());
myStackFrames = newFrame;
fireIntervalAdded(this, 0, getSize());
}
@Override
public int getSize() {
return myStackFrames.size();
}
@Override
public StackFrameItem getElementAt(int index) {
return myStackFrames.get(index);
}
}
}
@@ -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,44 +15,28 @@
*/
package com.intellij.debugger.memory.ui;
import com.intellij.openapi.project.Project;
import com.intellij.debugger.engine.DebugProcessImpl;
import com.intellij.debugger.memory.utils.StackFrameItem;
import com.intellij.openapi.ui.popup.JBPopup;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.xdebugger.impl.ui.DebuggerUIUtil;
import org.jetbrains.annotations.NotNull;
import com.intellij.debugger.memory.utils.StackFrameItem;
import java.util.List;
public class StackFramePopup {
private final Project myProject;
private final List<StackFrameItem> myStackFrame;
private final GlobalSearchScope myScope;
public StackFramePopup(@NotNull Project project,
@NotNull List<StackFrameItem> stack,
@NotNull GlobalSearchScope searchScope) {
myProject = project;
myStackFrame = stack;
myScope = searchScope;
}
public void show() {
StackFrameList list = new StackFrameList(myProject, myStackFrame, myScope);
list.addListSelectionListener(e -> {
if (!e.getValueIsAdjusting()) {
list.navigateToSelectedValue(false);
}
});
JBPopup popup = JBPopupFactory.getInstance().createListPopupBuilder(list)
public static void show(@NotNull List<StackFrameItem> stack, DebugProcessImpl debugProcess) {
StackFrameList list = new StackFrameList(debugProcess);
list.setFrameItems(stack, () -> DebuggerUIUtil.invokeLater(() -> {
JBPopup popup = JBPopupFactory.getInstance().createListPopupBuilder(list)
.setTitle("Select stack frame")
.setAutoSelectIfEmpty(true)
.setResizable(false)
.setItemChoosenCallback(() -> list.navigateToSelectedValue(true))
.createPopup();
list.setSelectedIndex(1);
popup.showInFocusCenter();
list.setSelectedIndex(1);
popup.showInFocusCenter();
}));
}
}
@@ -15,27 +15,16 @@
*/
package com.intellij.debugger.memory.utils;
import com.intellij.debugger.SourcePosition;
import com.intellij.debugger.engine.DebugProcessImpl;
import com.intellij.debugger.engine.JavaStackFrame;
import com.intellij.debugger.engine.PositionManagerImpl;
import com.intellij.debugger.engine.SuspendContextImpl;
import com.intellij.debugger.engine.*;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.impl.DebuggerUtilsEx;
import com.intellij.debugger.jdi.DecompiledLocalVariable;
import com.intellij.debugger.jdi.LocalVariablesUtil;
import com.intellij.debugger.jdi.StackFrameProxyImpl;
import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
import com.intellij.debugger.jdi.*;
import com.intellij.debugger.settings.NodeRendererSettings;
import com.intellij.debugger.ui.breakpoints.StackCapturingLineBreakpoint;
import com.intellij.debugger.ui.impl.watch.MessageDescriptor;
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.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.ui.ColoredTextContainer;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.util.PlatformIcons;
@@ -58,99 +47,24 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
public class StackFrameItem extends XStackFrame {
public class StackFrameItem {
private static final Logger LOG = Logger.getInstance(StackFrameItem.class);
private final Project myProject;
private final GlobalSearchScope mySearchScope;
private final Location myLocation;
private final List<XNamedValue> myVariables;
private final String myFilePath;
private final String myMethodName;
private final int myLineNumber;
private List<XNamedValue> myVariables = null;
private boolean myFirst;
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;
}
PsiElement element = psiClass.getNavigationElement();
// see IDEA-137167, prefer not compiled elements
if (element instanceof PsiCompiledElement) {
PsiElement fileElement = psiClass.getContainingFile().getNavigationElement();
if (!(fileElement instanceof PsiCompiledElement)) {
element = fileElement;
}
}
SourcePosition position = SourcePosition.createFromLine(element.getContainingFile(), myLineNumber - 1);
PsiFile psiFile = psiClass.getContainingFile().getOriginalFile();
if (psiFile instanceof PsiCompiledFile) {
position = new PositionManagerImpl.ClsSourcePosition(position, myLineNumber - 1);
}
return DebuggerUtilsEx.toXSourcePosition(position);
});
public StackFrameItem(Location location, List<XNamedValue> variables) {
myLocation = location;
myVariables = variables;
}
@NotNull
public String path() {
return myFilePath;
}
@NotNull
public String methodName() {
return myMethodName;
}
@NotNull
public String className() {
return StringUtil.getShortName(myFilePath);
}
@NotNull
public String packageName() {
return StringUtil.getPackageName(myFilePath);
return myLocation.declaringType().name();
}
public int line() {
return myLineNumber;
}
@Override
public void customizePresentation(@NotNull ColoredTextContainer component) {
component.setIcon(myFirst ? AllIcons.Actions.Menu_cut : JBUI.scale(EmptyIcon.create(6)));
component.append(String.format("%s:%d, %s", myMethodName, myLineNumber, className()), SimpleTextAttributes.REGULAR_ATTRIBUTES);
String packageName = packageName();
if (!packageName.trim().isEmpty()) {
component.append(String.format(" (%s)", packageName), SimpleTextAttributes.GRAYED_ITALIC_ATTRIBUTES);
}
}
@Nullable
@Override
public XSourcePosition getSourcePosition() {
return mySourcePosition.getValue();
}
void addVariable(XNamedValue var) {
if (myVariables == null) {
myVariables = new ArrayList<>();
}
myVariables.add(var);
return myLocation.lineNumber();
}
@NotNull
@@ -162,19 +76,9 @@ public class StackFrameItem extends XStackFrame {
List<StackFrameItem> res = new ArrayList<>();
for (StackFrameProxyImpl frame : threadReferenceProxy.frames()) {
try {
Location loc = frame.location();
DebugProcessImpl debugProcess = suspendContext.getDebugProcess();
StackFrameItem frameItem = new StackFrameItem(debugProcess.getProject(),
debugProcess.getSearchScope(),
loc.declaringType().name(),
loc.method().name(),
loc.lineNumber());
if (res.isEmpty()) {
frameItem.myFirst = true;
}
res.add(frameItem);
List<XNamedValue> vars = null;
if (withVars) {
vars = new ArrayList<>();
List<StackFrameItem> relatedStack = StackCapturingLineBreakpoint.getRelatedStack(frame, suspendContext);
if (!ContainerUtil.isEmpty(relatedStack)) {
res.addAll(relatedStack);
@@ -184,7 +88,7 @@ public class StackFrameItem extends XStackFrame {
try {
ObjectReference thisObject = frame.thisObject();
if (thisObject != null) {
frameItem.addVariable(createVariable(thisObject, "this", VariableItem.VarType.OBJECT));
vars.add(createVariable(thisObject, "this", VariableItem.VarType.OBJECT));
}
}
catch (EvaluateException e) {
@@ -192,28 +96,25 @@ public class StackFrameItem extends XStackFrame {
}
try {
frame.visibleVariables().forEach(v -> {
for (LocalVariableProxyImpl v : frame.visibleVariables()) {
try {
Value value = frame.getValue(v);
VariableItem.VarType varType = VariableItem.VarType.OBJECT;
if (v.getVariable().isArgument()) {
varType = VariableItem.VarType.PARAM;
}
frameItem.addVariable(createVariable(value, v.name(), varType));
VariableItem.VarType varType = v.getVariable().isArgument() ? VariableItem.VarType.PARAM :VariableItem.VarType.OBJECT;
vars.add(createVariable(frame.getValue(v), v.name(), varType));
}
catch (EvaluateException e) {
LOG.debug(e);
}
});
}
}
catch (EvaluateException e) {
if (e.getCause() instanceof AbsentInformationException) {
frameItem.addVariable(JavaStackFrame.createMessageNode(MessageDescriptor.LOCAL_VARIABLES_INFO_UNAVAILABLE.getLabel(),
vars.add(JavaStackFrame.createMessageNode(MessageDescriptor.LOCAL_VARIABLES_INFO_UNAVAILABLE.getLabel(),
XDebuggerUIConstants.INFORMATION_MESSAGE_ICON));
// trying to collect values from variable slots
try {
for (Map.Entry<DecompiledLocalVariable, Value> entry : LocalVariablesUtil.fetchValues(frame, debugProcess).entrySet()) {
frameItem.addVariable(createVariable(entry.getValue(), entry.getKey().getDisplayName(), VariableItem.VarType.PARAM));
for (Map.Entry<DecompiledLocalVariable, Value> entry : LocalVariablesUtil
.fetchValues(frame, suspendContext.getDebugProcess()).entrySet()) {
vars.add(createVariable(entry.getValue(), entry.getKey().getDisplayName(), VariableItem.VarType.PARAM));
}
}
catch (Exception ex) {
@@ -225,6 +126,9 @@ public class StackFrameItem extends XStackFrame {
}
}
}
StackFrameItem frameItem = new StackFrameItem(frame.location(), vars);
res.add(frameItem);
}
catch (EvaluateException e) {
LOG.debug(e);
@@ -248,18 +152,6 @@ public class StackFrameItem extends XStackFrame {
return new VariableItem(name, type, valueText, varType);
}
@Override
public void computeChildren(@NotNull XCompositeNode node) {
if (myVariables != null) {
XValueChildrenList children = new XValueChildrenList();
myVariables.forEach(children::add);
node.addChildren(children, true);
}
else {
node.addChildren(XValueChildrenList.EMPTY, true);
}
}
private static class VariableItem extends XNamedValue {
enum VarType {PARAM, OBJECT}
@@ -281,4 +173,76 @@ public class StackFrameItem extends XStackFrame {
node.setPresentation(icon, type, myValue, false);
}
}
public CapturedStackFrame createFrame(DebugProcessImpl debugProcess) {
return new CapturedStackFrame(debugProcess, this);
}
public static class CapturedStackFrame extends XStackFrame implements JVMStackFrameInfoProvider {
private final XSourcePosition mySourcePosition;
private final boolean myIsSynthetic;
private final boolean myIsInLibraryContent;
private final String myPath;
private final String myMethodName;
private final int myLineNumber;
private final List<XNamedValue> myVariables;
public CapturedStackFrame(DebugProcessImpl debugProcess, StackFrameItem item) {
DebuggerManagerThreadImpl.assertIsManagerThread();
mySourcePosition = DebuggerUtilsEx.toXSourcePosition(debugProcess.getPositionManager().getSourcePosition(item.myLocation));
myIsSynthetic = DebuggerUtils.isSynthetic(item.myLocation.method());
myIsInLibraryContent =
DebuggerUtilsEx.isInLibraryContent(mySourcePosition != null ? mySourcePosition.getFile() : null, debugProcess.getProject());
myPath = item.path();
myMethodName = item.myLocation.method().name();
myLineNumber = item.line();
myVariables = item.myVariables;
}
@Nullable
@Override
public XSourcePosition getSourcePosition() {
return mySourcePosition;
}
public boolean isSynthetic() {
return myIsSynthetic;
}
public boolean isInLibraryContent() {
return myIsInLibraryContent;
}
@Override
public void customizePresentation(@NotNull ColoredTextContainer component) {
//component.setIcon(myFirst ? AllIcons.Actions.Menu_cut : JBUI.scale(EmptyIcon.create(6)));
component.setIcon(JBUI.scale(EmptyIcon.create(6)));
component.append(String.format("%s:%d, %s", myMethodName, myLineNumber, StringUtil.getShortName(myPath)), getAttributes());
String packageName = StringUtil.getPackageName(myPath);
if (!packageName.trim().isEmpty()) {
component.append(String.format(" (%s)", packageName), SimpleTextAttributes.GRAYED_ITALIC_ATTRIBUTES);
}
}
@Override
public void computeChildren(@NotNull XCompositeNode node) {
if (myVariables != null) {
XValueChildrenList children = new XValueChildrenList();
myVariables.forEach(children::add);
node.addChildren(children, true);
}
else {
node.addChildren(XValueChildrenList.EMPTY, true);
}
}
private SimpleTextAttributes getAttributes() {
if (isSynthetic() || isInLibraryContent()) {
return SimpleTextAttributes.GRAYED_ATTRIBUTES;
}
return SimpleTextAttributes.REGULAR_ATTRIBUTES;
}
}
}
@@ -35,10 +35,7 @@ import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.NullableLazyValue;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.ColoredTextContainer;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.xdebugger.XSourcePosition;
import com.sun.jdi.Location;
import com.sun.jdi.Method;
import com.sun.jdi.ObjectReference;
@@ -49,7 +46,10 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.java.debugger.breakpoints.properties.JavaMethodBreakpointProperties;
import java.util.*;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
/**
@@ -66,7 +66,7 @@ public class StackCapturingLineBreakpoint extends WildcardMethodBreakpoint {
public static final Key<List<StackCapturingLineBreakpoint>> CAPTURE_BREAKPOINTS = Key.create("CAPTURE_BREAKPOINTS");
public static final Key<Map<ObjectReference, List<StackFrameItem>>> CAPTURED_STACKS = Key.create("CAPTURED_STACKS");
private static final int MAX_STORED_STACKS = 1000;
private static final int MAX_STACK_LENGTH = 500;
public static final int MAX_STACK_LENGTH = 500;
private final JavaMethodBreakpointProperties myProperties = new JavaMethodBreakpointProperties();
@@ -124,10 +124,7 @@ public class StackCapturingLineBreakpoint extends WildcardMethodBreakpoint {
if (key instanceof ObjectReference) {
List<StackFrameItem> frames = StackFrameItem.createFrames(suspendContext.getThread(), suspendContext, true);
if (frames.size() > MAX_STACK_LENGTH) {
ArrayList<StackFrameItem> truncated = new ArrayList<>(MAX_STACK_LENGTH + 1);
truncated.addAll(frames.subList(0, MAX_STACK_LENGTH));
truncated.add(TOO_MANY_FRAMES);
frames = truncated;
frames = frames.subList(0, MAX_STACK_LENGTH);
}
stacks.put((ObjectReference)key, frames);
}
@@ -139,19 +136,6 @@ public class StackCapturingLineBreakpoint extends WildcardMethodBreakpoint {
return false;
}
private static StackFrameItem TOO_MANY_FRAMES = new StackFrameItem(null, null, "", "", -1) {
@Nullable
@Override
public XSourcePosition getSourcePosition() {
return null;
}
@Override
public void customizePresentation(@NotNull ColoredTextContainer component) {
component.append("Too many frames, the rest is truncated...", SimpleTextAttributes.REGULAR_ITALIC_ATTRIBUTES);
}
};
@Override
public StreamEx matchingMethods(StreamEx<Method> methods, DebugProcessImpl debugProcess) {
String methodName = getMethodName();
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2015 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.
@@ -46,6 +46,11 @@ public abstract class DebuggerFramesList extends JBList implements OccurenceNavi
getEmptyText().setText(XDebuggerBundle.message("debugger.frames.not.available"));
}
@Override
public void setModel(ListModel model) {
// do not allow to change model (e.g. to FilteringListModel)
}
@Override
public DefaultListModel getModel() {
return (DefaultListModel)super.getModel();