diff --git a/Ghidra/Features/FunctionGraph/src/main/help/help/topics/FunctionGraphPlugin/Function_Graph.html b/Ghidra/Features/FunctionGraph/src/main/help/help/topics/FunctionGraphPlugin/Function_Graph.html
index feb5225643..12185e4d0c 100644
--- a/Ghidra/Features/FunctionGraph/src/main/help/help/topics/FunctionGraphPlugin/Function_Graph.html
+++ b/Ghidra/Features/FunctionGraph/src/main/help/help/topics/FunctionGraphPlugin/Function_Graph.html
@@ -729,6 +729,12 @@
+
The Max Nodes option limits how many nodes will be generated while building the + graph. When the limit is reached, the graph loading will be cancelled and an error message + will be displayed at the bottom of the graph. This option is useful if user navigation + triggers large graphs loading that causes the Function Graph to lock-up the UI while it is + open. +
The Scroll Wheel Pans option signals to move the graph vertical when scrolling the
mouse scroll wheel. Disabling this option restores the original function graph scroll wheel
diff --git a/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/graph/FunctionGraphFactory.java b/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/graph/FunctionGraphFactory.java
index 90ba27424f..76db409604 100644
--- a/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/graph/FunctionGraphFactory.java
+++ b/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/graph/FunctionGraphFactory.java
@@ -316,7 +316,17 @@ public class FunctionGraphFactory {
CodeBlockIterator iterator = blockModel.getCodeBlocksContaining(addresses, monitor);
monitor.initialize(addresses.getNumAddresses());
- for (; iterator.hasNext();) {
+ FunctionGraphOptions fgOptions = controller.getFunctionGraphOptions();
+ int maxNodes = fgOptions.getMaxNodes();
+
+ for (int i = 0; iterator.hasNext(); i++) {
+
+ if (i > maxNodes) {
+ String message =
+ "Graph is too large; options limit set to %s nodes".formatted(maxNodes);
+ throw new CancelledException(message);
+ }
+
CodeBlock codeBlock = iterator.next();
FlowType flowType = codeBlock.getFlowType();
diff --git a/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/mvc/FunctionGraphOptions.java b/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/mvc/FunctionGraphOptions.java
index ea6e0bc29d..d2c76bba2b 100644
--- a/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/mvc/FunctionGraphOptions.java
+++ b/Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/mvc/FunctionGraphOptions.java
@@ -80,12 +80,16 @@ public class FunctionGraphOptions extends VisualGraphOptions {
private static final String DEFAULT_GROUP_BACKGROUND_COLOR_DESCRPTION =
"The default background color applied to newly created group vertices";
- private static final String UPDATE_GROUP_AND_UNGROUP_COLORS =
+ private static final String UPDATE_GROUP_AND_UNGROUP_COLORS_KEY =
"Update Vertex Colors When Grouping";
private static final String UPDATE_GROUP_AND_UNGROUP_COLORS_DESCRIPTION =
"Signals that any user color changes to a group vertex will apply that same color to " +
"all grouped vertices as well.";
+ private static final String MAX_NODES_KEY = "Max Nodes";
+ private static final String MAX_NODES_DESCRIPTION =
+ "The maximum number of nodes to process before cancelling graph loading.";
+
private boolean updateGroupColorsAutomatically = true;
//@formatter:off
@@ -102,6 +106,7 @@ public class FunctionGraphOptions extends VisualGraphOptions {
private GColor unconditionalJumpEdgeHighlightColor = new GColor("color.bg.plugin.functiongraph.edge.jump.unconditional.highlight");
//@formatter:on
+ private int maxNodes = 1000;
private boolean useFullSizeTooltip = false;
private RelayoutOption relayoutOption = RelayoutOption.VERTEX_GROUPING_CHANGES;
@@ -122,6 +127,10 @@ public class FunctionGraphOptions extends VisualGraphOptions {
return updateGroupColorsAutomatically;
}
+ public int getMaxNodes() {
+ return maxNodes;
+ }
+
public Color getFallthroughEdgeColor() {
return fallthroughEdgeColor;
}
@@ -178,12 +187,14 @@ public class FunctionGraphOptions extends VisualGraphOptions {
options.registerThemeColorBinding(DEFAULT_GROUP_BACKGROUND_COLOR_KEY,
defaultGroupBackgroundColor.getId(), help, DEFAULT_GROUP_BACKGROUND_COLOR_DESCRPTION);
- options.registerOption(UPDATE_GROUP_AND_UNGROUP_COLORS, updateGroupColorsAutomatically,
+ options.registerOption(UPDATE_GROUP_AND_UNGROUP_COLORS_KEY, updateGroupColorsAutomatically,
help, UPDATE_GROUP_AND_UNGROUP_COLORS_DESCRIPTION);
options.registerOption(USE_FULL_SIZE_TOOLTIP_KEY, useFullSizeTooltip, help,
USE_FULL_SIZE_TOOLTIP_DESCRIPTION);
+ options.registerOption(MAX_NODES_KEY, maxNodes, help, MAX_NODES_DESCRIPTION);
+
options.registerThemeColorBinding(EDGE_COLOR_CONDITIONAL_JUMP_KEY,
conditionalJumpEdgeColor.getId(), help, "Conditional jump edge color");
@@ -220,7 +231,9 @@ public class FunctionGraphOptions extends VisualGraphOptions {
useFullSizeTooltip = options.getBoolean(USE_FULL_SIZE_TOOLTIP_KEY, useFullSizeTooltip);
updateGroupColorsAutomatically =
- options.getBoolean(UPDATE_GROUP_AND_UNGROUP_COLORS, updateGroupColorsAutomatically);
+ options.getBoolean(UPDATE_GROUP_AND_UNGROUP_COLORS_KEY, updateGroupColorsAutomatically);
+
+ maxNodes = options.getInt(MAX_NODES_KEY, maxNodes);
SetCancelledException indicates that the user cancelled
* the current operation.
*/
public class CancelledException extends UsrException {
+ public static final String DEFAULT_MESSAGE = "Operation cancelled";
+
/**
* Default constructor. Message indicates 'Operation cancelled'.
*/
public CancelledException() {
- super("Operation cancelled");
+ super(DEFAULT_MESSAGE);
}
public CancelledException(String msg) {
super(msg);
}
-
+
+ /**
+ * {@return true if the message of this exception is {@value #DEFAULT_MESSAGE}}
+ */
+ public boolean isDefaultMessage() {
+ return DEFAULT_MESSAGE.equals(getMessage());
+ }
+
}