Skip to content

Commit e94dc2a

Browse files
committed
Implement grouping of the stack frames
The rules how the frames are categorized are not editable as of now, just a single action is visible to switch on-off the behaviour, with enough javadocs. Category is a record, so it can be freely extended in the future.
1 parent f7ab749 commit e94dc2a

File tree

20 files changed

+787
-48
lines changed

20 files changed

+787
-48
lines changed

org.eclipse.jdt.debug.ui/plugin.properties

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,9 @@ showMonitorThreadInfo.tooltip=Show the Thread & Monitor Information
178178
showNullEntriesAction.label=Show &Null Array Entries
179179
showNullEntriesAction.tooltip=Show Null Array Entries
180180

181+
collapseStackFrames.label=Collapse Stack Frames
182+
collapseStackFrames.tooltip=Hide less relevant stack frames
183+
181184
stepIntoSelectionHyperlinkDetector.label=Step Into Selection
182185
stepIntoSelectionHyperlinkDetector.description=Performs the step into selection command on demand via a hyperlink
183186

org.eclipse.jdt.debug.ui/plugin.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2225,6 +2225,14 @@
22252225
style="toggle"
22262226
menubarPath="org.eclipse.jdt.debug.ui.LaunchView.javaSubmenu/javaPart"
22272227
id="org.eclipse.jdt.debug.ui.launchViewActions.ShowMonitorThreadInfo"/>
2228+
<action
2229+
helpContextId="collapse_stack_frames_action_context"
2230+
label="%collapseStackFrames.label"
2231+
tooltip="%collapseStackFrames.tooltip"
2232+
class="org.eclipse.jdt.internal.debug.ui.actions.CollapseStackFramesAction"
2233+
style="toggle"
2234+
menubarPath="org.eclipse.jdt.debug.ui.LaunchView.javaSubmenu/javaPart"
2235+
id="org.eclipse.jdt.debug.ui.launchViewActions.CollapseStackFrames"/>
22282236
<menu
22292237
id="org.eclipse.jdt.debug.ui.LaunchView.javaSubmenu"
22302238
label="%LaunchViewJavaSubmenu.label"

org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/DebugUIMessages.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ public class DebugUIMessages extends NLS {
155155
public static String JDIModelPresentation__garbage_collected_object__6;
156156
public static String JDIModelPresentation__obsolete_method_in__1;
157157
public static String JDIModelPresentation__terminated__2;
158+
public static String JDIModelPresentation_collapsed_frames;
158159

159160
public static String JDIModelPresentation_117;
160161

org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/DebugUIMessages.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ JDIModelPresentation_modification_72=\ [modification]
117117
JDIModelPresentation_native_method=[native method]
118118
JDIModelPresentation_not_available=not available
119119
JDIModelPresentation_Suspend_VM=[Suspend VM]
120+
JDIModelPresentation_collapsed_frames={0} collapsed frames
120121

121122
###############################################################################
122123
# Thread label keys are built programmatically

org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/IJDIPreferencesConstants.java

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,10 @@ public interface IJDIPreferencesConstants {
7979
*/
8080
public static final String PREF_STEP_THRU_FILTERS = IJavaDebugUIConstants.PLUGIN_ID + ".step_thru_filters"; //$NON-NLS-1$
8181

82+
public static final String PREF_COLLAPSE_STACK_FRAMES = IJavaDebugUIConstants.PLUGIN_ID + ".collapse_stack_frames"; //$NON-NLS-1$
83+
8284
/**
83-
* List of active step filters. A String containing a comma
84-
* separated list of fully qualified type names/patterns.
85+
* List of active step filters. A String containing a comma separated list of fully qualified type names/patterns.
8586
*/
8687
public static final String PREF_ACTIVE_FILTERS_LIST = IJavaDebugUIConstants.PLUGIN_ID + ".active_filters"; //$NON-NLS-1$
8788

@@ -91,6 +92,38 @@ public interface IJDIPreferencesConstants {
9192
*/
9293
public static final String PREF_INACTIVE_FILTERS_LIST = IJavaDebugUIConstants.PLUGIN_ID + ".inactive_filters"; //$NON-NLS-1$
9394

95+
/**
96+
* List of active filters for custom stack frame categorization. A String containing a comma separated list of fully qualified type
97+
* names/patterns.
98+
*
99+
* @since 3.22
100+
*/
101+
public static final String PREF_ACTIVE_CUSTOM_FRAME_FILTER_LIST = IJavaDebugUIConstants.PLUGIN_ID + ".active_custom_frames_filters"; //$NON-NLS-1$
102+
103+
/**
104+
* List of inactive filters for custom stack frame categorization. A String containing a comma separated list of fully qualified type
105+
* names/patterns.
106+
*
107+
* @since 3.22
108+
*/
109+
public static final String PREF_INACTIVE_CUSTOM_FRAME_FILTER_LIST = IJavaDebugUIConstants.PLUGIN_ID + ".inactive_custom_frames_filters"; //$NON-NLS-1$
110+
111+
/**
112+
* List of active filters for custom stack frame categorization. A String containing a comma separated list of fully qualified type
113+
* names/patterns.
114+
*
115+
* @since 3.22
116+
*/
117+
public static final String PREF_ACTIVE_PLATFORM_FRAME_FILTER_LIST = IJavaDebugUIConstants.PLUGIN_ID + ".active_platform_frames_filters"; //$NON-NLS-1$
118+
119+
/**
120+
* List of inactive filters for custom stack frame categorization. A String containing a comma separated list of fully qualified type
121+
* names/patterns.
122+
*
123+
* @since 3.22
124+
*/
125+
public static final String PREF_INACTIVE_PLATFORM_FRAME_FILTER_LIST = IJavaDebugUIConstants.PLUGIN_ID + ".inactive_platform_frames_filters"; //$NON-NLS-1$
126+
94127
/**
95128
* Boolean preference controlling whether to alert
96129
* with a dialog when hot code replace fails.

org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIDebugUIPlugin.java

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2000, 2022 IBM Corporation and others.
2+
* Copyright (c) 2000, 2024 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -57,6 +57,8 @@
5757
import org.eclipse.jdt.debug.core.IJavaVariable;
5858
import org.eclipse.jdt.debug.core.JDIDebugModel;
5959
import org.eclipse.jdt.debug.ui.IJavaDebugUIConstants;
60+
import org.eclipse.jdt.internal.debug.core.JDIDebugPlugin;
61+
import org.eclipse.jdt.internal.debug.core.model.GroupedStackFrame;
6062
import org.eclipse.jdt.internal.debug.ui.breakpoints.ExceptionInspector;
6163
import org.eclipse.jdt.internal.debug.ui.breakpoints.JavaBreakpointTypeAdapterFactory;
6264
import org.eclipse.jdt.internal.debug.ui.classpath.ClasspathEntryAdapterFactory;
@@ -135,6 +137,8 @@ public class JDIDebugUIPlugin extends AbstractUIPlugin {
135137

136138
private IDebugModelPresentation fUtilPresentation;
137139

140+
private StackFrameCategorizer stackFrameCategorizer;
141+
138142
/**
139143
* Java Debug UI listeners
140144
*/
@@ -382,6 +386,7 @@ public void start(BundleContext context) throws Exception {
382386
manager.registerAdapters(monitorFactory, JavaOwningThread.class);
383387
manager.registerAdapters(monitorFactory, JavaWaitingThread.class);
384388
manager.registerAdapters(monitorFactory, IJavaStackFrame.class);
389+
manager.registerAdapters(monitorFactory, GroupedStackFrame.class);
385390

386391
IAdapterFactory targetFactory = new TargetAdapterFactory();
387392
manager.registerAdapters(targetFactory, IJavaDebugTarget.class);
@@ -391,6 +396,7 @@ public void start(BundleContext context) throws Exception {
391396

392397
IAdapterFactory showInFactory = new JavaDebugShowInAdapterFactory();
393398
manager.registerAdapters(showInFactory, IJavaStackFrame.class);
399+
manager.registerAdapters(showInFactory, GroupedStackFrame.class);
394400

395401
IAdapterFactory columnFactory = new ColumnPresentationAdapterFactory();
396402
manager.registerAdapters(columnFactory, IJavaVariable.class);
@@ -419,7 +425,7 @@ public void prepareToSave(ISaveContext sc) throws CoreException {}
419425
public void rollback(ISaveContext sc) {}
420426
@Override
421427
public void saving(ISaveContext sc) throws CoreException {
422-
IEclipsePreferences prefs = InstanceScope.INSTANCE.getNode(getUniqueIdentifier());
428+
IEclipsePreferences prefs = getInstancePreferences();
423429
if(prefs != null) {
424430
try {
425431
prefs.flush();
@@ -431,6 +437,11 @@ public void saving(ISaveContext sc) throws CoreException {
431437
}
432438
});
433439
JavaDebugOptionsManager.getDefault().startup();
440+
441+
IEclipsePreferences instancePreferences = getInstancePreferences();
442+
stackFrameCategorizer = new StackFrameCategorizer(Platform.getPreferencesService(), instancePreferences);
443+
instancePreferences.addPreferenceChangeListener(stackFrameCategorizer);
444+
JDIDebugPlugin.getDefault().setStackFrameCategorizer(stackFrameCategorizer::categorize);
434445
}
435446

436447
/* (non-Javadoc)
@@ -440,6 +451,10 @@ public void saving(ISaveContext sc) throws CoreException {
440451
public void stop(BundleContext context) throws Exception {
441452
try {
442453
setShuttingDown(true);
454+
455+
getInstancePreferences().removePreferenceChangeListener(stackFrameCategorizer);
456+
JDIDebugPlugin.getDefault().setStackFrameCategorizer(null);
457+
443458
JDIDebugModel.removeHotCodeReplaceListener(fHCRListener);
444459
JavaDebugOptionsManager.getDefault().shutdown();
445460
if (fImageDescriptorRegistry != null) {
@@ -686,5 +701,16 @@ public JavaTextTools getJavaTextTools() {
686701
}
687702
return fTextTools;
688703
}
704+
705+
/**
706+
* @return the plugin's preferences from the InstanceScope.
707+
*/
708+
private static IEclipsePreferences getInstancePreferences() {
709+
return InstanceScope.INSTANCE.getNode(getUniqueIdentifier());
710+
}
711+
712+
public StackFrameCategorizer getStackFrameCategorizer() {
713+
return stackFrameCategorizer;
714+
}
689715
}
690716

org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIDebugUIPreferenceInitializer.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2004, 2020 IBM Corporation and others.
2+
* Copyright (c) 2004, 2024 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -46,6 +46,14 @@ public void initializeDefaultPreferences() {
4646
store.setDefault(IJDIPreferencesConstants.PREF_INACTIVE_FILTERS_LIST, "com.ibm.*,com.sun.*,java.*,javax.*,jdk.*,jrockit.*,org.omg.*,sun.*,sunw.*"); //$NON-NLS-1$
4747
store.setDefault(IJDIPreferencesConstants.PREF_STEP_THRU_FILTERS, true);
4848

49+
// Grouping stack frames
50+
store.setDefault(IJDIPreferencesConstants.PREF_COLLAPSE_STACK_FRAMES, true);
51+
52+
store.setDefault(IJDIPreferencesConstants.PREF_ACTIVE_PLATFORM_FRAME_FILTER_LIST, "java.*,javax.*,jdk.*,sun.*,sunw.*,org.junit.*,org.eclipse.jdt.internal.*"); //$NON-NLS-1$
53+
store.setDefault(IJDIPreferencesConstants.PREF_INACTIVE_PLATFORM_FRAME_FILTER_LIST, ""); //$NON-NLS-1$
54+
store.setDefault(IJDIPreferencesConstants.PREF_ACTIVE_CUSTOM_FRAME_FILTER_LIST, ""); //$NON-NLS-1$
55+
store.setDefault(IJDIPreferencesConstants.PREF_INACTIVE_CUSTOM_FRAME_FILTER_LIST, ""); //$NON-NLS-1$
56+
4957
store.setDefault(IDebugUIConstants.ID_VARIABLE_VIEW + "." + IJDIPreferencesConstants.PREF_SHOW_CONSTANTS, false); //$NON-NLS-1$
5058
store.setDefault(IDebugUIConstants.ID_EXPRESSION_VIEW + "." + IJDIPreferencesConstants.PREF_SHOW_CONSTANTS, false); //$NON-NLS-1$
5159
store.setDefault(IDebugUIConstants.ID_VARIABLE_VIEW + "." + IJDIPreferencesConstants.PREF_SHOW_STATIC_VARIABLES, false); //$NON-NLS-1$

org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIModelPresentation.java

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
import org.eclipse.jdt.internal.debug.core.breakpoints.JavaExceptionBreakpoint;
7373
import org.eclipse.jdt.internal.debug.core.logicalstructures.JDIAllInstancesValue;
7474
import org.eclipse.jdt.internal.debug.core.logicalstructures.JDIReturnValueVariable;
75+
import org.eclipse.jdt.internal.debug.core.model.GroupedStackFrame;
7576
import org.eclipse.jdt.internal.debug.core.model.JDIDebugModelMessages;
7677
import org.eclipse.jdt.internal.debug.core.model.JDIReferenceListEntryVariable;
7778
import org.eclipse.jdt.internal.debug.core.model.JDIReferenceListValue;
@@ -151,6 +152,8 @@ public class JDIModelPresentation extends LabelProvider implements IDebugModelPr
151152

152153
private JavaElementLabelProvider fJavaLabelProvider;
153154

155+
private StackFramePresentationProvider fStackFrameProvider;
156+
154157
public JDIModelPresentation() {
155158
super();
156159
}
@@ -165,6 +168,9 @@ public void dispose() {
165168
fJavaLabelProvider.dispose();
166169
}
167170
fAttributes.clear();
171+
if (fStackFrameProvider != null) {
172+
fStackFrameProvider.close();
173+
}
168174
}
169175

170176
/**
@@ -244,6 +250,8 @@ public String getText(Object item) {
244250
return getJavaOwningTreadText((JavaOwningThread)item);
245251
} else if (item instanceof JavaWaitingThread) {
246252
return getJavaWaitingTreadText((JavaWaitingThread)item);
253+
} else if (item instanceof GroupedStackFrame groupping) {
254+
return getFormattedString(DebugUIMessages.JDIModelPresentation_collapsed_frames, String.valueOf(groupping.getFrameCount()));
247255
} else if (item instanceof NoMonitorInformationElement) {
248256
return DebugUIMessages.JDIModelPresentation_5;
249257
} else {
@@ -744,7 +752,10 @@ public Image getImage(Object item) {
744752
return DebugUITools.getImage(IDebugUIConstants.IMG_OBJS_THREAD_RUNNING);
745753
}
746754
}
747-
if (item instanceof IJavaStackFrame || item instanceof IJavaThread || item instanceof IJavaDebugTarget) {
755+
if (item instanceof IJavaStackFrame) {
756+
return getStackFrameImage((IJavaStackFrame) item);
757+
}
758+
if (item instanceof IJavaThread || item instanceof IJavaDebugTarget) {
748759
return getDebugElementImage(item);
749760
}
750761
if (item instanceof IJavaValue) {
@@ -944,6 +955,30 @@ protected Image getDebugElementImage(Object element) {
944955
return getDebugImage(image, flags);
945956
}
946957

958+
/**
959+
* Returns the image associated with the given {@link IJavaStackFrame}, decorated with overlays, if the stack frame is out of sync
960+
* ({@link IJavaStackFrame#isOutOfSynch()} or synchronized ({@link IJavaStackFrame#isSynchronized()}). The base image is acquired from the
961+
* {@link StackFramePresentationProvider}.
962+
*/
963+
private Image getStackFrameImage(IJavaStackFrame stackFrame) {
964+
var image = getStackFrameProvider().getStackFrameImage(stackFrame);
965+
if (image == null) {
966+
image = DebugUITools.getDefaultImageDescriptor(stackFrame);
967+
}
968+
969+
int flags = 0;
970+
try {
971+
if (stackFrame.isOutOfSynch()) {
972+
flags = JDIImageDescriptor.IS_OUT_OF_SYNCH;
973+
} else if (!stackFrame.isObsolete() && stackFrame.isSynchronized()) {
974+
flags = JDIImageDescriptor.SYNCHRONIZED;
975+
}
976+
} catch (DebugException e) {
977+
// no need to log errors - elements may no longer exist by the time we render them
978+
}
979+
980+
return getDebugImage(image, flags);
981+
}
947982
/**
948983
* Returns the image associated with the given element or <code>null</code>
949984
* if none is defined.
@@ -966,21 +1001,11 @@ protected Image getExpressionImage(Object expression) {
9661001
}
9671002

9681003
/**
969-
* Returns the adornment flags for the given element.
970-
* These flags are used to render appropriate overlay
971-
* icons for the element.
1004+
* Returns the adornment flags for the given element. These flags are used to render appropriate overlay icons for the element. It only supports
1005+
* {@link IJavaThread} and {@link IJavaDebugTarget}, for other types it always returns 0.
9721006
*/
9731007
private int computeJDIAdornmentFlags(Object element) {
9741008
try {
975-
if (element instanceof IJavaStackFrame) {
976-
IJavaStackFrame javaStackFrame = ((IJavaStackFrame)element);
977-
if (javaStackFrame.isOutOfSynch()) {
978-
return JDIImageDescriptor.IS_OUT_OF_SYNCH;
979-
}
980-
if (!javaStackFrame.isObsolete() && javaStackFrame.isSynchronized()) {
981-
return JDIImageDescriptor.SYNCHRONIZED;
982-
}
983-
}
9841009
if (element instanceof IJavaThread) {
9851010
int flag= 0;
9861011
IJavaThread javaThread = ((IJavaThread)element);
@@ -2065,6 +2090,16 @@ protected JavaElementLabelProvider getJavaLabelProvider() {
20652090
return fJavaLabelProvider;
20662091
}
20672092

2093+
/**
2094+
* @return a {@link StackFramePresentationProvider} which responsible to classify stack frames into categories and could provide category specific
2095+
* visual representations.
2096+
*/
2097+
private StackFramePresentationProvider getStackFrameProvider() {
2098+
if (fStackFrameProvider == null) {
2099+
fStackFrameProvider = new StackFramePresentationProvider();
2100+
}
2101+
return fStackFrameProvider;
2102+
}
20682103
/**
20692104
* Returns whether the given field variable has the same name as any variables
20702105
*/

0 commit comments

Comments
 (0)