GP-6837 - Function Graph - Added an option to limit the number of nodes

This commit is contained in:
dragonmacher
2026-05-15 16:53:03 -04:00
parent 510c4d14c7
commit 37f27db75d
5 changed files with 53 additions and 12 deletions
@@ -729,6 +729,12 @@
<BR>
<BR>
<P>The <B>Max Nodes</B> 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.
</P>
<P>The <B>Scroll Wheel Pans</B> option signals to move the graph vertical when scrolling the
mouse scroll wheel. Disabling this option restores the original function graph scroll wheel
@@ -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();
@@ -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);
Set<Entry<String, FGLayoutOptions>> entries = layoutOptionsByName.entrySet();
for (Entry<String, FGLayoutOptions> entry : entries) {
@@ -4,9 +4,9 @@
* 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.
@@ -107,7 +107,12 @@ public class FunctionGraphRunnable implements SwingRunnable {
"Finished creating graph for \"" + validatedFunction.getName() + "\"");
}
catch (CancelledException e) {
String message = "Cancelled graph for \"" + validatedFunction.getName() + "\"";
if (!e.isDefaultMessage()) {
message = e.getMessage();
}
graphData = new EmptyFunctionGraphData(message);
monitor.setMessage(message);
}
@@ -1,13 +1,12 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* 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.
@@ -16,22 +15,30 @@
*/
package ghidra.util.exception;
/**
* <code>CancelledException</code> 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());
}
}