This action is offered to resolve a "missing module" console message. It is equivalent to Map Module To on the missing module.
+
Show Sections Table
+
+
This actions is always available. By default the sections table (bottom) is showing. Some
+ debuggers do not offer section information, and even for those that do, it can be expensive to
+ retrieve it. The visibility of the section table is controlled by toggling this action.
+
Filter Sections by Module
This action is always available. By default the bottom table displays all sections in the
diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerModulesPlugin/images/DebuggerModulesPlugin.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerModulesPlugin/images/DebuggerModulesPlugin.png
index 72fe042d4b..2d1b78b3d7 100644
Binary files a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerModulesPlugin/images/DebuggerModulesPlugin.png and b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerModulesPlugin/images/DebuggerModulesPlugin.png differ
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerResources.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerResources.java
index a06598d846..57bded0203 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerResources.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerResources.java
@@ -813,40 +813,6 @@ public interface DebuggerResources {
}
}
- interface ImportMissingModuleAction {
- String NAME = "Import Missing Module";
- String DESCRIPTION = "Import the missing module from disk";
- Icon ICON = ICON_IMPORT;
- String HELP_ANCHOR = "import_missing_module";
-
- static ActionBuilder builder(Plugin owner) {
- String ownerName = owner.getName();
- return new ActionBuilder(NAME, ownerName)
- .description(DESCRIPTION)
- .toolBarIcon(ICON)
- .popupMenuIcon(ICON)
- .popupMenuPath(NAME)
- .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
- }
- }
-
- interface MapMissingModuleAction {
- String NAME = "Map Missing Module";
- String DESCRIPTION = "Map the missing module to an existing import";
- Icon ICON = ICON_MAP_MODULES;
- String HELP_ANCHOR = "map_missing_module";
-
- static ActionBuilder builder(Plugin owner) {
- String ownerName = owner.getName();
- return new ActionBuilder(NAME, ownerName)
- .description(DESCRIPTION)
- .toolBarIcon(ICON)
- .popupMenuIcon(ICON)
- .popupMenuPath(NAME)
- .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
- }
- }
-
interface FollowsCurrentThreadAction {
String NAME = "Follows Selected Thread";
String DESCRIPTION = "Register tracking follows selected thread (and contents" +
@@ -1864,86 +1830,6 @@ public interface DebuggerResources {
}
}
- interface LimitToCurrentSnapAction {
- String NAME = "Limit to Current Snap";
- String DESCRIPTION = "Choose whether displayed objects must be alive at the current snap";
- String GROUP = GROUP_GENERAL;
- String HELP_ANCHOR = "limit_to_current_snap";
-
- static ToggleActionBuilder builder(Plugin owner) {
- String ownerName = owner.getName();
- return new ToggleActionBuilder(NAME, ownerName)
- .description(DESCRIPTION)
- .menuPath(NAME)
- .menuGroup(GROUP)
- .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
- }
- }
-
- interface ShowHiddenAction {
- String NAME = "Show Hidden";
- String DESCRIPTION = "Choose whether to display hidden children";
- String GROUP = GROUP_GENERAL;
- String HELP_ANCHOR = "show_hidden";
-
- static ToggleActionBuilder builder(Plugin owner) {
- String ownerName = owner.getName();
- return new ToggleActionBuilder(NAME, ownerName)
- .description(DESCRIPTION)
- .menuPath(NAME)
- .menuGroup(GROUP)
- .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
- }
- }
-
- interface ShowPrimitivesInTreeAction {
- String NAME = "Show Primitives in Tree";
- String DESCRIPTION = "Choose whether to display primitive values in the tree";
- String GROUP = GROUP_GENERAL;
- String HELP_ANCHOR = "show_primitives";
-
- static ToggleActionBuilder builder(Plugin owner) {
- String ownerName = owner.getName();
- return new ToggleActionBuilder(NAME, ownerName)
- .description(DESCRIPTION)
- .menuPath(NAME)
- .menuGroup(GROUP)
- .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
- }
- }
-
- interface ShowMethodsInTreeAction {
- String NAME = "Show Methods in Tree";
- String DESCRIPTION = "Choose whether to display methods in the tree";
- String GROUP = GROUP_GENERAL;
- String HELP_ANCHOR = "show_methods";
-
- static ToggleActionBuilder builder(Plugin owner) {
- String ownerName = owner.getName();
- return new ToggleActionBuilder(NAME, ownerName)
- .description(DESCRIPTION)
- .menuPath(NAME)
- .menuGroup(GROUP)
- .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
- }
- }
-
- interface FollowLinkAction {
- String NAME = "Follow Link";
- String DESCRIPTION = "Navigate to the link target";
- String GROUP = GROUP_GENERAL;
- String HELP_ANCHOR = "follow_link";
-
- static ActionBuilder builder(Plugin owner) {
- String ownerName = owner.getName();
- return new ActionBuilder(NAME, ownerName)
- .description(DESCRIPTION)
- .popupMenuPath(NAME)
- .popupMenuGroup(GROUP)
- .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
- }
- }
-
public abstract class AbstractDebuggerConnectionsNode extends GTreeNode {
@Override
public String getName() {
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/DebuggerModelProvider.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/DebuggerModelProvider.java
index 0ff19363ec..d185e040ff 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/DebuggerModelProvider.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/DebuggerModelProvider.java
@@ -20,6 +20,7 @@ import java.awt.event.*;
import java.lang.invoke.MethodHandles;
import java.util.List;
import java.util.Objects;
+import java.util.function.Function;
import java.util.stream.Collectors;
import javax.swing.*;
@@ -27,12 +28,15 @@ import javax.swing.*;
import docking.*;
import docking.action.DockingAction;
import docking.action.ToggleDockingAction;
+import docking.action.builder.ActionBuilder;
+import docking.action.builder.ToggleActionBuilder;
import docking.widgets.table.RangeCursorTableHeaderRenderer.SeekListener;
import docking.widgets.tree.support.GTreeSelectionEvent.EventOrigin;
import generic.theme.GColor;
+import generic.theme.GIcon;
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
-import ghidra.app.plugin.core.debug.gui.DebuggerResources.*;
+import ghidra.app.plugin.core.debug.gui.DebuggerResources.CloneWindowAction;
import ghidra.app.plugin.core.debug.gui.MultiProviderSaveBehavior.SaveableProvider;
import ghidra.app.plugin.core.debug.gui.model.AbstractQueryTablePanel.CellActivationListener;
import ghidra.app.plugin.core.debug.gui.model.ObjectTableModel.ObjectRow;
@@ -42,13 +46,13 @@ import ghidra.app.plugin.core.debug.gui.model.PathTableModel.PathRow;
import ghidra.app.services.DebuggerTraceManagerService;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.framework.options.SaveState;
-import ghidra.framework.plugintool.AutoConfigState;
-import ghidra.framework.plugintool.AutoService;
+import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.annotation.AutoConfigStateField;
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
import ghidra.trace.model.Lifespan;
import ghidra.trace.model.Trace;
import ghidra.trace.model.target.*;
+import ghidra.util.HelpLocation;
import ghidra.util.Msg;
public class DebuggerModelProvider extends ComponentProvider implements SaveableProvider {
@@ -59,10 +63,144 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable
private static final String KEY_DEBUGGER_COORDINATES = "DebuggerCoordinates";
private static final String KEY_PATH = "Path";
+ interface ShowObjectsTreeAction {
+ String NAME = "Show Objects Tree";
+ Icon ICON = new GIcon("icon.debugger.model.tree.objects");
+ String DESCRIPTION = "Toggle display of the Objects Tree pane";
+ String GROUP = DebuggerResources.GROUP_VIEWS;
+ String ORDER = "1";
+ String HELP_ANCHOR = "show_objects_tree";
+
+ static ToggleActionBuilder builder(Plugin owner) {
+ String ownerName = owner.getName();
+ return new ToggleActionBuilder(NAME, ownerName)
+ .description(DESCRIPTION)
+ .toolBarIcon(ICON)
+ .toolBarGroup(GROUP, ORDER)
+ .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
+ }
+ }
+
+ interface ShowElementsTableAction {
+ String NAME = "Show Elements Table";
+ Icon ICON = new GIcon("icon.debugger.model.table.elements");
+ String DESCRIPTION = "Toggle display of the Elements Table pane";
+ String GROUP = DebuggerResources.GROUP_VIEWS;
+ String ORDER = "2";
+ String HELP_ANCHOR = "show_elements_table";
+
+ static ToggleActionBuilder builder(Plugin owner) {
+ String ownerName = owner.getName();
+ return new ToggleActionBuilder(NAME, ownerName)
+ .description(DESCRIPTION)
+ .toolBarIcon(ICON)
+ .toolBarGroup(GROUP, ORDER)
+ .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
+ }
+ }
+
+ interface ShowAttributesTableAction {
+ String NAME = "Show Attributes Table";
+ Icon ICON = new GIcon("icon.debugger.model.table.attributes");
+ String DESCRIPTION = "Toggle display of the Attributes Table pane";
+ String GROUP = DebuggerResources.GROUP_VIEWS;
+ String ORDER = "3";
+ String HELP_ANCHOR = "show_attributes_table";
+
+ static ToggleActionBuilder builder(Plugin owner) {
+ String ownerName = owner.getName();
+ return new ToggleActionBuilder(NAME, ownerName)
+ .description(DESCRIPTION)
+ .toolBarIcon(ICON)
+ .toolBarGroup(GROUP, ORDER)
+ .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
+ }
+ }
+
+ interface LimitToCurrentSnapAction {
+ String NAME = "Limit to Current Snap";
+ String DESCRIPTION = "Choose whether displayed objects must be alive at the current snap";
+ String GROUP = DebuggerResources.GROUP_GENERAL;
+ String HELP_ANCHOR = "limit_to_current_snap";
+
+ static ToggleActionBuilder builder(Plugin owner) {
+ String ownerName = owner.getName();
+ return new ToggleActionBuilder(NAME, ownerName)
+ .description(DESCRIPTION)
+ .menuPath(NAME)
+ .menuGroup(GROUP)
+ .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
+ }
+ }
+
+ interface ShowHiddenAction {
+ String NAME = "Show Hidden";
+ String DESCRIPTION = "Choose whether to display hidden children";
+ String GROUP = DebuggerResources.GROUP_GENERAL;
+ String HELP_ANCHOR = "show_hidden";
+
+ static ToggleActionBuilder builder(Plugin owner) {
+ String ownerName = owner.getName();
+ return new ToggleActionBuilder(NAME, ownerName)
+ .description(DESCRIPTION)
+ .menuPath(NAME)
+ .menuGroup(GROUP)
+ .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
+ }
+ }
+
+ interface ShowPrimitivesInTreeAction {
+ String NAME = "Show Primitives in Tree";
+ String DESCRIPTION = "Choose whether to display primitive values in the tree";
+ String GROUP = DebuggerResources.GROUP_GENERAL;
+ String HELP_ANCHOR = "show_primitives";
+
+ static ToggleActionBuilder builder(Plugin owner) {
+ String ownerName = owner.getName();
+ return new ToggleActionBuilder(NAME, ownerName)
+ .description(DESCRIPTION)
+ .menuPath(NAME)
+ .menuGroup(GROUP)
+ .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
+ }
+ }
+
+ interface ShowMethodsInTreeAction {
+ String NAME = "Show Methods in Tree";
+ String DESCRIPTION = "Choose whether to display methods in the tree";
+ String GROUP = DebuggerResources.GROUP_GENERAL;
+ String HELP_ANCHOR = "show_methods";
+
+ static ToggleActionBuilder builder(Plugin owner) {
+ String ownerName = owner.getName();
+ return new ToggleActionBuilder(NAME, ownerName)
+ .description(DESCRIPTION)
+ .menuPath(NAME)
+ .menuGroup(GROUP)
+ .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
+ }
+ }
+
+ interface FollowLinkAction {
+ String NAME = "Follow Link";
+ String DESCRIPTION = "Navigate to the link target";
+ String GROUP = DebuggerResources.GROUP_GENERAL;
+ String HELP_ANCHOR = "follow_link";
+
+ static ActionBuilder builder(Plugin owner) {
+ String ownerName = owner.getName();
+ return new ActionBuilder(NAME, ownerName)
+ .description(DESCRIPTION)
+ .popupMenuPath(NAME)
+ .popupMenuGroup(GROUP)
+ .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
+ }
+ }
+
private final DebuggerModelPlugin plugin;
private final boolean isClone;
- private JPanel mainPanel = new JPanel(new BorderLayout());
+ private JPanel mainPanel;
static class MyTextField extends JTextField {
// This one can be reflected for testing
@@ -74,6 +212,7 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable
protected MyTextField pathField;
protected JButton goButton;
+ protected JPanel queryPanel;
protected ObjectsTreePanel objectsTreePanel;
protected ObjectsTablePanel elementsTablePanel;
protected PathsTablePanel attributesTablePanel;
@@ -86,6 +225,17 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable
@SuppressWarnings("unused")
private final AutoService.Wiring autoServiceWiring;
+ @AutoConfigStateField
+ private boolean showObjectsTree = true;
+ @AutoConfigStateField
+ private boolean showElementsTable = true;
+ @AutoConfigStateField
+ private boolean showAttributesTable = true;
+ @AutoConfigStateField
+ private double lrResizeWeight = 0.2;
+ @AutoConfigStateField
+ private double tbResizeWeight = 0.7;
+
@AutoConfigStateField
private boolean limitToSnap = true;
@AutoConfigStateField
@@ -96,6 +246,9 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable
private boolean showMethodsInTree = false;
DockingAction actionCloneWindow;
+ ToggleDockingAction actionShowObjectsTree;
+ ToggleDockingAction actionShowElementsTable;
+ ToggleDockingAction actionShowAttributesTable;
ToggleDockingAction actionLimitToCurrentSnap;
ToggleDockingAction actionShowHidden;
ToggleDockingAction actionShowPrimitivesInTree;
@@ -156,7 +309,117 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable
super.removeFromTool();
}
+ protected static double computeResizeWeight(JSplitPane split) {
+ Function axis = switch (split.getOrientation()) {
+ case JSplitPane.HORIZONTAL_SPLIT -> (dim -> dim.width);
+ case JSplitPane.VERTICAL_SPLIT -> (dim -> dim.height);
+ default -> throw new AssertionError();
+ };
+
+ // This method is off by a little, and I don't know why, but I don't care.
+
+ Component lComp = split.getLeftComponent();
+ int lMin = axis.apply(lComp.getMinimumSize());
+ int lSize = axis.apply(lComp.getSize());
+ Component rComp = split.getRightComponent();
+ int rMin = axis.apply(rComp.getMinimumSize());
+ int rSize = axis.apply(rComp.getSize());
+
+ int totalExtra = lSize + rSize - lMin - rMin;
+ int lExtra = lSize - lMin;
+
+ return (double) lExtra / totalExtra;
+ }
+
+ protected JSplitPane createLrSplit() {
+ JSplitPane split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
+ split.setResizeWeight(lrResizeWeight);
+ split.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, pce -> {
+ lrResizeWeight = computeResizeWeight(split);
+ });
+ return split;
+ }
+
+ protected JSplitPane createTbSplit() {
+ JSplitPane split = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
+ split.setResizeWeight(tbResizeWeight);
+ split.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, pce -> {
+ tbResizeWeight = computeResizeWeight(split);
+ });
+ return split;
+ }
+
+ protected JPanel createLabeledElementsTable() {
+ JPanel panel = new JPanel(new BorderLayout());
+ panel.add(elementsTablePanel);
+ panel.add(new JLabel("Elements"), BorderLayout.NORTH);
+ return panel;
+ }
+
+ protected JPanel createLabeledAttributesTable() {
+ JPanel panel = new JPanel(new BorderLayout());
+ panel.add(attributesTablePanel);
+ panel.add(new JLabel("Attributes"), BorderLayout.NORTH);
+ return panel;
+ }
+
+ protected void rebuildPanels() {
+ if (mainPanel == null) {
+ return;
+ }
+
+ try (ListenerSuppressor suppressor = objectsTreePanel.suppressShowingListener()) {
+ mainPanel.removeAll();
+ mainPanel.add(queryPanel, BorderLayout.NORTH);
+
+ if (showObjectsTree && showElementsTable && showAttributesTable) {
+ JSplitPane lrSplit = createLrSplit();
+ mainPanel.add(lrSplit, BorderLayout.CENTER);
+ JSplitPane tbSplit = createTbSplit();
+ lrSplit.setRightComponent(tbSplit);
+ lrSplit.setLeftComponent(objectsTreePanel);
+ tbSplit.setLeftComponent(createLabeledElementsTable());
+ tbSplit.setRightComponent(createLabeledAttributesTable());
+ }
+ else if (showObjectsTree && showElementsTable) {
+ JSplitPane lrSplit = createLrSplit();
+ mainPanel.add(lrSplit, BorderLayout.CENTER);
+ lrSplit.setLeftComponent(objectsTreePanel);
+ lrSplit.setRightComponent(elementsTablePanel);
+ }
+ else if (showObjectsTree && showAttributesTable) {
+ JSplitPane lrSplit = createLrSplit();
+ mainPanel.add(lrSplit, BorderLayout.CENTER);
+ lrSplit.setLeftComponent(objectsTreePanel);
+ lrSplit.setRightComponent(attributesTablePanel);
+ }
+ else if (showElementsTable && showAttributesTable) {
+ JSplitPane tbSplit = createTbSplit();
+ tbSplit.setLeftComponent(createLabeledElementsTable());
+ tbSplit.setRightComponent(createLabeledAttributesTable());
+ mainPanel.add(tbSplit, BorderLayout.CENTER);
+ }
+ else if (showObjectsTree) {
+ mainPanel.add(objectsTreePanel);
+ }
+ else if (showElementsTable) {
+ mainPanel.add(elementsTablePanel);
+ }
+ else if (showAttributesTable) {
+ mainPanel.add(attributesTablePanel);
+ }
+ else {
+ // The actions should not allow this, but in case it happens, help the user out.
+ mainPanel.add(new JLabel("""
+ Well, you should probably enable at least one panel.
+ Use the local toolbar buttons."""));
+ }
+ }
+ mainPanel.revalidate();
+ }
+
protected void buildMainPanel() {
+ mainPanel = new JPanel(new BorderLayout());
pathField = new MyTextField();
pathField.setInputVerifier(new InputVerifier() {
@Override
@@ -207,27 +470,12 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable
tbSplit.setResizeWeight(0.7);
lrSplit.setRightComponent(tbSplit);
- JPanel queryPanel = new JPanel(new BorderLayout());
+ queryPanel = new JPanel(new BorderLayout());
queryPanel.add(new JLabel("Path: "), BorderLayout.WEST);
queryPanel.add(pathField, BorderLayout.CENTER);
queryPanel.add(goButton, BorderLayout.EAST);
- JPanel labeledElementsTablePanel = new JPanel(new BorderLayout());
- labeledElementsTablePanel.add(elementsTablePanel);
- labeledElementsTablePanel.add(new JLabel("Elements"), BorderLayout.NORTH);
-
- JPanel labeledAttributesTablePanel = new JPanel(new BorderLayout());
- labeledAttributesTablePanel.add(attributesTablePanel);
- labeledAttributesTablePanel.add(new JLabel("Attributes"), BorderLayout.NORTH);
-
- lrSplit.setLeftComponent(objectsTreePanel);
- tbSplit.setLeftComponent(labeledElementsTablePanel);
- tbSplit.setRightComponent(labeledAttributesTablePanel);
-
- mainPanel.add(queryPanel, BorderLayout.NORTH);
- mainPanel.add(lrSplit, BorderLayout.CENTER);
-
objectsTreePanel.addTreeSelectionListener(evt -> {
Trace trace = current.getTrace();
if (trace == null) {
@@ -240,6 +488,7 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable
if (!sel.isEmpty()) {
myActionContext = new DebuggerObjectActionContext(sel.stream()
.map(n -> n.getValue())
+ .filter(o -> o != null) // Root for no trace would return null
.collect(Collectors.toList()),
this, objectsTreePanel);
}
@@ -251,7 +500,8 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable
return;
}
TraceObjectValue value = sel.get(0).getValue();
- setPath(value.getCanonicalPath(), objectsTreePanel);
+ setPath(value == null ? TraceObjectKeyPath.of() : value.getCanonicalPath(),
+ objectsTreePanel);
});
objectsTreePanel.tree.addMouseListener(new MouseAdapter() {
@Override
@@ -320,6 +570,8 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable
elementsTablePanel.addSeekListener(seekListener);
attributesTablePanel.addSeekListener(seekListener);
+
+ rebuildPanels();
}
private void activateObjectSelectedInTree() {
@@ -348,6 +600,18 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable
.enabledWhen(c -> current.getTrace() != null)
.onAction(c -> activatedCloneWindow())
.buildAndInstallLocal(this);
+ actionShowObjectsTree = ShowObjectsTreeAction.builder(plugin)
+ .onAction(this::toggledShowObjectsTree)
+ .selected(showObjectsTree)
+ .buildAndInstallLocal(this);
+ actionShowElementsTable = ShowElementsTableAction.builder(plugin)
+ .onAction(this::toggledShowElementsTable)
+ .selected(showElementsTable)
+ .buildAndInstallLocal(this);
+ actionShowAttributesTable = ShowAttributesTableAction.builder(plugin)
+ .onAction(this::toggledShowAttributesTable)
+ .selected(showAttributesTable)
+ .buildAndInstallLocal(this);
actionLimitToCurrentSnap = LimitToCurrentSnapAction.builder(plugin)
.onAction(this::toggledLimitToCurrentSnap)
.buildAndInstallLocal(this);
@@ -404,6 +668,18 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable
plugin.getTool().showComponentProvider(clone, true);
}
+ private void toggledShowObjectsTree(ActionContext ctx) {
+ setShowObjectsTree(actionShowObjectsTree.isSelected());
+ }
+
+ private void toggledShowElementsTable(ActionContext ctx) {
+ setShowElementsTable(actionShowElementsTable.isSelected());
+ }
+
+ private void toggledShowAttributesTable(ActionContext ctx) {
+ setShowAttributesTable(actionShowAttributesTable.isSelected());
+ }
+
private void toggledLimitToCurrentSnap(ActionContext ctx) {
setLimitToCurrentSnap(actionLimitToCurrentSnap.isSelected());
}
@@ -496,8 +772,6 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable
elementsTablePanel.goToCoordinates(coords);
attributesTablePanel.goToCoordinates(coords);
- checkPath();
-
if (isClone) {
return;
}
@@ -549,6 +823,7 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable
traceManager.activateObject(object);
return;
}
+ plugin.getTool().setStatusInfo("No such object at path " + path, true);
}
}
@@ -566,27 +841,55 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable
objectsTreePanel.repaint();
elementsTablePanel.setQuery(ModelQuery.elementsOf(path));
attributesTablePanel.setQuery(ModelQuery.attributesOf(path));
-
- checkPath();
}
public void setPath(TraceObjectKeyPath path) {
setPath(path, null);
}
- protected void checkPath() {
- if (Trace.isLegacy(current.getTrace())) {
- return;
- }
- if (objectsTreePanel.getNode(path) == null) {
- plugin.getTool().setStatusInfo("No such object at path " + path, true);
- }
- }
-
public TraceObjectKeyPath getPath() {
return path;
}
+ protected void doSetShowObjectsTree(boolean showObjectsTree) {
+ this.showObjectsTree = showObjectsTree;
+ actionShowObjectsTree.setSelected(showObjectsTree);
+ rebuildPanels();
+ }
+
+ protected void doSetShowElementsTable(boolean showElementsTable) {
+ this.showElementsTable = showElementsTable;
+ actionShowElementsTable.setSelected(showElementsTable);
+ rebuildPanels();
+ }
+
+ protected void doSetShowAttributesTable(boolean showAttributesTable) {
+ this.showAttributesTable = showAttributesTable;
+ actionShowAttributesTable.setSelected(showAttributesTable);
+ rebuildPanels();
+ }
+
+ public void setShowObjectsTree(boolean showObjectsTree) {
+ if (this.showObjectsTree == showObjectsTree) {
+ return;
+ }
+ doSetShowObjectsTree(showObjectsTree);
+ }
+
+ public void setShowElementsTable(boolean showElementsTable) {
+ if (this.showElementsTable == showElementsTable) {
+ return;
+ }
+ doSetShowElementsTable(showElementsTable);
+ }
+
+ public void setShowAttributesTable(boolean showAttributesTable) {
+ if (this.showAttributesTable == showAttributesTable) {
+ return;
+ }
+ doSetShowAttributesTable(showAttributesTable);
+ }
+
protected void doSetLimitToCurrentSnap(boolean limitToSnap) {
this.limitToSnap = limitToSnap;
actionLimitToCurrentSnap.setSelected(limitToSnap);
@@ -680,6 +983,12 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable
@Override
public void readConfigState(SaveState saveState) {
CONFIG_STATE_HANDLER.readConfigState(this, saveState);
+
+ actionShowObjectsTree.setSelected(showObjectsTree);
+ actionShowElementsTable.setSelected(showElementsTable);
+ actionShowAttributesTable.setSelected(showAttributesTable);
+ rebuildPanels();
+
doSetLimitToCurrentSnap(limitToSnap);
doSetShowHidden(showHidden);
doSetShowPrimitivesInTree(showPrimitivesInTree);
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/ListenerSuppressor.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/ListenerSuppressor.java
new file mode 100644
index 0000000000..8bd713dd6b
--- /dev/null
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/ListenerSuppressor.java
@@ -0,0 +1,21 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * 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 ghidra.app.plugin.core.debug.gui.model;
+
+public interface ListenerSuppressor extends AutoCloseable {
+ @Override
+ void close();
+}
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/ObjectsTreePanel.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/ObjectsTreePanel.java
index a4d82dd1a6..926c7ae10c 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/ObjectsTreePanel.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/ObjectsTreePanel.java
@@ -22,6 +22,8 @@ import java.util.stream.*;
import javax.swing.JPanel;
import javax.swing.JTree;
+import javax.swing.event.AncestorEvent;
+import javax.swing.event.AncestorListener;
import javax.swing.tree.TreePath;
import docking.widgets.tree.GTree;
@@ -95,10 +97,55 @@ public class ObjectsTreePanel extends JPanel {
}
}
+ protected class ListenerForShowing implements AncestorListener {
+ boolean showing = false;
+
+ @Override
+ public void ancestorRemoved(AncestorEvent event) {
+ updateShowing();
+ }
+
+ @Override
+ public void ancestorMoved(AncestorEvent event) {
+ updateShowing();
+ }
+
+ @Override
+ public void ancestorAdded(AncestorEvent event) {
+ updateShowing();
+ }
+
+ public void updateShowing() {
+ setShowing(ObjectsTreePanel.this.isShowing());
+ }
+
+ private void setShowing(boolean showing) {
+ if (this.showing == showing) {
+ return;
+ }
+ this.showing = showing;
+ showingChanged(showing);
+ }
+ }
+
+ protected class ListenerForShowingSuppressor implements ListenerSuppressor {
+ public ListenerForShowingSuppressor() {
+ removeAncestorListener(listenerForShowing);
+ }
+
+ @Override
+ public void close() {
+ addAncestorListener(listenerForShowing);
+ listenerForShowing.updateShowing();
+ }
+ }
+
protected final ObjectTreeModel treeModel;
protected final ObjectGTree tree;
+ protected boolean showing = false;
protected DebuggerCoordinates current = DebuggerCoordinates.NOWHERE;
+ protected DebuggerCoordinates previous = DebuggerCoordinates.NOWHERE;
protected boolean limitToSnap = true;
protected boolean showHidden = false;
protected boolean showPrimitives = false;
@@ -107,8 +154,13 @@ public class ObjectsTreePanel extends JPanel {
protected Color diffColor = DebuggerResources.COLOR_VALUE_CHANGED;
protected Color diffColorSel = DebuggerResources.COLOR_VALUE_CHANGED_SEL;
+ protected final ListenerForShowing listenerForShowing = new ListenerForShowing();
+
public ObjectsTreePanel() {
super(new BorderLayout());
+
+ addAncestorListener(listenerForShowing);
+
treeModel = createModel();
tree = new ObjectGTree(treeModel.getRoot());
@@ -116,6 +168,10 @@ public class ObjectsTreePanel extends JPanel {
add(tree, BorderLayout.CENTER);
}
+ public ListenerSuppressor suppressShowingListener() {
+ return new ListenerForShowingSuppressor();
+ }
+
protected ObjectTreeModel createModel() {
return new ObjectTreeModel();
}
@@ -124,6 +180,20 @@ public class ObjectsTreePanel extends JPanel {
return new KeepTreeState(tree);
}
+ protected void showingChanged(boolean showing) {
+ this.showing = showing;
+ updateTreeModelForCoordinates();
+ updateTreeModelForSpan();
+ updateTreeModelForShowHidden();
+ updateTreeModelForShowPrimitives();
+ updateTreeModelForShowMethods();
+ if (showing) {
+ // Not going to restore the actual selection
+ selectCurrent();
+ }
+ // Restore expansion? Nah.
+ }
+
protected Trace computeDiffTrace(Trace current, Trace previous) {
if (current == null) {
return null;
@@ -138,13 +208,22 @@ public class ObjectsTreePanel extends JPanel {
if (DebuggerCoordinates.equalsIgnoreRecorderAndView(current, coords)) {
return;
}
- DebuggerCoordinates previous = current;
- this.current = coords;
+ previous = current;
+ current = coords;
if (previous.getSnap() == current.getSnap() &&
previous.getTrace() == current.getTrace() &&
previous.getObject() == current.getObject()) {
return;
}
+ updateTreeModelForCoordinates();
+ }
+
+ protected void updateTreeModelForCoordinates() {
+ if (!showing) {
+ // Clear it out and have it remove its listeners
+ treeModel.setTrace(null);
+ return;
+ }
try (KeepTreeState keep = keepTreeState()) {
treeModel.setDiffTrace(computeDiffTrace(current.getTrace(), previous.getTrace()));
treeModel.setTrace(current.getTrace());
@@ -163,6 +242,13 @@ public class ObjectsTreePanel extends JPanel {
return;
}
this.limitToSnap = limitToSnap;
+ updateTreeModelForSpan();
+ }
+
+ protected void updateTreeModelForSpan() {
+ if (!showing) {
+ return;
+ }
try (KeepTreeState keep = keepTreeState()) {
treeModel.setSpan(limitToSnap ? Lifespan.at(current.getSnap()) : Lifespan.ALL);
}
@@ -177,6 +263,13 @@ public class ObjectsTreePanel extends JPanel {
return;
}
this.showHidden = showHidden;
+ updateTreeModelForShowHidden();
+ }
+
+ protected void updateTreeModelForShowHidden() {
+ if (!showing) {
+ return;
+ }
try (KeepTreeState keep = keepTreeState()) {
treeModel.setShowHidden(showHidden);
}
@@ -191,6 +284,13 @@ public class ObjectsTreePanel extends JPanel {
return;
}
this.showPrimitives = showPrimitives;
+ updateTreeModelForShowPrimitives();
+ }
+
+ protected void updateTreeModelForShowPrimitives() {
+ if (!showing) {
+ return;
+ }
try (KeepTreeState keep = keepTreeState()) {
treeModel.setShowPrimitives(showPrimitives);
}
@@ -205,6 +305,13 @@ public class ObjectsTreePanel extends JPanel {
return;
}
this.showMethods = showMethods;
+ updateTreeModelForShowMethods();
+ }
+
+ protected void updateTreeModelForShowMethods() {
+ if (!showing) {
+ return;
+ }
try (KeepTreeState keep = keepTreeState()) {
treeModel.setShowMethods(showMethods);
}
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerModulesProvider.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerModulesProvider.java
index aad2a60ee3..ed7ecad5ff 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerModulesProvider.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerModulesProvider.java
@@ -28,12 +28,12 @@ import org.apache.commons.lang3.ArrayUtils;
import docking.*;
import docking.action.*;
-import docking.action.builder.ActionBuilder;
-import docking.action.builder.MultiStateActionBuilder;
+import docking.action.builder.*;
import docking.menu.ActionState;
import docking.menu.MultiStateDockingAction;
import docking.widgets.EventTrigger;
import docking.widgets.filechooser.GhidraFileChooser;
+import generic.theme.GIcon;
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
import ghidra.app.plugin.core.debug.gui.DebuggerBlockChooserDialog;
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
@@ -217,6 +217,58 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter {
}
}
+ interface ImportMissingModuleAction {
+ String NAME = "Import Missing Module";
+ String DESCRIPTION = "Import the missing module from disk";
+ Icon ICON = DebuggerResources.ICON_IMPORT;
+ String HELP_ANCHOR = "import_missing_module";
+
+ static ActionBuilder builder(Plugin owner) {
+ String ownerName = owner.getName();
+ return new ActionBuilder(NAME, ownerName)
+ .description(DESCRIPTION)
+ .toolBarIcon(ICON)
+ .popupMenuIcon(ICON)
+ .popupMenuPath(NAME)
+ .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
+ }
+ }
+
+ interface MapMissingModuleAction {
+ String NAME = "Map Missing Module";
+ String DESCRIPTION = "Map the missing module to an existing import";
+ Icon ICON = DebuggerResources.ICON_MAP_MODULES;
+ String HELP_ANCHOR = "map_missing_module";
+
+ static ActionBuilder builder(Plugin owner) {
+ String ownerName = owner.getName();
+ return new ActionBuilder(NAME, ownerName)
+ .description(DESCRIPTION)
+ .toolBarIcon(ICON)
+ .popupMenuIcon(ICON)
+ .popupMenuPath(NAME)
+ .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
+ }
+ }
+
+ interface ShowSectionsTableAction {
+ String NAME = "Show Sections Table";
+ Icon ICON = new GIcon("icon.debugger.modules.table.sections");
+ String DESCRIPTION = "Toggle display fo the Sections Table pane";
+ String GROUP = DebuggerResources.FilterAction.GROUP;
+ String ORDER = "1";
+ String HELP_ANCHOR = "show_sections_table";
+
+ static ToggleActionBuilder builder(Plugin owner) {
+ String ownerName = owner.getName();
+ return new ToggleActionBuilder(NAME, ownerName)
+ .description(DESCRIPTION)
+ .toolBarIcon(ICON)
+ .toolBarGroup(GROUP, ORDER)
+ .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
+ }
+ }
+
protected class ForMappingTraceListener extends TraceDomainObjectListener {
public ForMappingTraceListener(AutoMapSpec spec) {
for (TraceChangeType, ?> type : spec.getChangeTypes()) {
@@ -369,6 +421,7 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter {
private final AutoService.Wiring autoServiceWiring;
private final JSplitPane mainPanel = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
+ private final int defaultDividerSize = mainPanel.getDividerSize();
DebuggerModulesPanel modulesPanel;
DebuggerLegacyModulesPanel legacyModulesPanel;
@@ -397,8 +450,14 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter {
MultiStateDockingAction actionAutoMap;
private final AutoMapSpec defaultAutoMapSpec =
AutoMapSpec.fromConfigName(ByModuleAutoMapSpec.CONFIG_NAME);
+
@AutoConfigStateField(codec = AutoMapSpecConfigFieldCodec.class)
AutoMapSpec autoMapSpec = defaultAutoMapSpec;
+ @AutoConfigStateField
+ boolean showSectionsTable = true;
+ @AutoConfigStateField
+ boolean filterSectionsByModules = false;
+
boolean cueAutoMap;
private ForMappingTraceListener forMappingListener;
@@ -407,6 +466,7 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter {
SelectAddressesAction actionSelectAddresses;
ImportFromFileSystemAction actionImportFromFileSystem;
+ ToggleDockingAction actionShowSectionsTable;
// TODO: Save the state of this toggle? Not really compelled.
ToggleDockingAction actionFilterSectionsByModules;
DockingAction actionSelectCurrent;
@@ -481,8 +541,7 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter {
}
protected boolean isFilterSectionsByModules() {
- // TODO: Make this a proper field and save it to tool state
- return actionFilterSectionsByModules.isSelected();
+ return filterSectionsByModules;
}
void modulesPanelContextChanged() {
@@ -576,6 +635,10 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter {
actionSelectAddresses = new SelectAddressesAction();
actionImportFromFileSystem = new ImportFromFileSystemAction();
+ actionShowSectionsTable = ShowSectionsTableAction.builder(plugin)
+ .onAction(this::toggledShowSectionsTable)
+ .selected(showSectionsTable)
+ .buildAndInstallLocal(this);
actionFilterSectionsByModules = FilterAction.builder(plugin)
.description("Filter sections to those in selected modules")
.helpLocation(new HelpLocation(plugin.getName(), "filter_by_module"))
@@ -714,10 +777,42 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter {
mapModuleTo(context.getModule());
}
+ private void toggledShowSectionsTable(ActionContext ignored) {
+ setShowSectionsTable(actionShowSectionsTable.isSelected());
+ }
+
+ public void setShowSectionsTable(boolean showSectionsTable) {
+ if (this.showSectionsTable == showSectionsTable) {
+ return;
+ }
+ doSetShowSectionsTable(showSectionsTable);
+ }
+
+ protected void doSetShowSectionsTable(boolean showSectionsTable) {
+ this.showSectionsTable = showSectionsTable;
+ actionShowSectionsTable.setSelected(showSectionsTable);
+ mainPanel.setDividerSize(showSectionsTable ? defaultDividerSize : 0);
+ sectionsPanel.setVisible(showSectionsTable);
+ legacySectionsPanel.setVisible(showSectionsTable);
+ mainPanel.resetToPreferredSizes();
+ }
+
private void toggledFilter(ActionContext ignored) {
- boolean filtered = isFilterSectionsByModules();
- sectionsPanel.setFilteredBySelectedModules(filtered);
- legacySectionsPanel.setFilteredBySelectedModules(filtered);
+ setFilterSectionsByModules(actionFilterSectionsByModules.isSelected());
+ }
+
+ public void setFilterSectionsByModules(boolean filterSectionsByModules) {
+ if (this.filterSectionsByModules == filterSectionsByModules) {
+ return;
+ }
+ doSetFilterSectionsByModules(filterSectionsByModules);
+ }
+
+ protected void doSetFilterSectionsByModules(boolean filterSectionsByModules) {
+ this.filterSectionsByModules = filterSectionsByModules;
+ actionFilterSectionsByModules.setSelected(filterSectionsByModules);
+ sectionsPanel.setFilteredBySelectedModules(filterSectionsByModules);
+ legacySectionsPanel.setFilteredBySelectedModules(filterSectionsByModules);
}
private void activatedSelectCurrent(ActionContext ignored) {
@@ -1111,5 +1206,7 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter {
public void readConfigState(SaveState saveState) {
CONFIG_STATE_HANDLER.readConfigState(this, saveState);
actionAutoMap.setCurrentActionStateByUserData(autoMapSpec);
+ doSetFilterSectionsByModules(filterSectionsByModules);
+ doSetShowSectionsTable(showSectionsTable);
}
}
diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/table-a.png b/Ghidra/Debug/Debugger/src/main/resources/images/table-a.png
new file mode 100644
index 0000000000..0847802ca9
Binary files /dev/null and b/Ghidra/Debug/Debugger/src/main/resources/images/table-a.png differ
diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/table-e.png b/Ghidra/Debug/Debugger/src/main/resources/images/table-e.png
new file mode 100644
index 0000000000..3820673688
Binary files /dev/null and b/Ghidra/Debug/Debugger/src/main/resources/images/table-e.png differ
diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/table-s.png b/Ghidra/Debug/Debugger/src/main/resources/images/table-s.png
new file mode 100644
index 0000000000..7cb371d1d2
Binary files /dev/null and b/Ghidra/Debug/Debugger/src/main/resources/images/table-s.png differ
diff --git a/Ghidra/Debug/Debugger/src/main/svg/table-a.svg b/Ghidra/Debug/Debugger/src/main/svg/table-a.svg
new file mode 100644
index 0000000000..9bc6be4412
--- /dev/null
+++ b/Ghidra/Debug/Debugger/src/main/svg/table-a.svg
@@ -0,0 +1,56 @@
+
+
diff --git a/Ghidra/Debug/Debugger/src/main/svg/table-e.svg b/Ghidra/Debug/Debugger/src/main/svg/table-e.svg
new file mode 100644
index 0000000000..4798d8ec7c
--- /dev/null
+++ b/Ghidra/Debug/Debugger/src/main/svg/table-e.svg
@@ -0,0 +1,56 @@
+
+
diff --git a/Ghidra/Debug/Debugger/src/main/svg/table-s.svg b/Ghidra/Debug/Debugger/src/main/svg/table-s.svg
new file mode 100644
index 0000000000..49d8253c33
--- /dev/null
+++ b/Ghidra/Debug/Debugger/src/main/svg/table-s.svg
@@ -0,0 +1,56 @@
+
+
diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/model/DebuggerModelProviderTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/model/DebuggerModelProviderTest.java
index 3bdc1a709a..d30dfa9a9a 100644
--- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/model/DebuggerModelProviderTest.java
+++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/model/DebuggerModelProviderTest.java
@@ -117,6 +117,7 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerTest
public void setUpModelProviderTest() throws Exception {
modelPlugin = addPlugin(tool, DebuggerModelPlugin.class);
modelProvider = waitForComponentProvider(DebuggerModelProvider.class);
+ modelProvider.setLimitToCurrentSnap(false);
}
@After
@@ -343,12 +344,12 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerTest
}
@Test
- public void testSetPathNoExist() throws Throwable {
+ public void testActivatePathNoExist() throws Throwable {
createTraceAndPopulateObjects();
traceManager.activateTrace(tb.trace);
waitForSwing();
- modelProvider.setPath(TraceObjectKeyPath.parse("Processes[0].NoSuch"));
+ modelProvider.activatePath(TraceObjectKeyPath.parse("Processes[0].NoSuch"));
waitForTasks();
assertEquals("No such object at path Processes[0].NoSuch", tool.getStatusInfo());