Merge remote-tracking branch

'origin/GP-6837-dragonmacher-fg-node-limit-option--SQUASHED'
(Closes #3411)
This commit is contained in:
Ryan Kurtz
2026-05-17 19:13:20 -04:00
5 changed files with 53 additions and 12 deletions
@@ -729,6 +729,12 @@
<BR> <BR>
<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 <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 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); CodeBlockIterator iterator = blockModel.getCodeBlocksContaining(addresses, monitor);
monitor.initialize(addresses.getNumAddresses()); 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(); CodeBlock codeBlock = iterator.next();
FlowType flowType = codeBlock.getFlowType(); FlowType flowType = codeBlock.getFlowType();
@@ -80,12 +80,16 @@ public class FunctionGraphOptions extends VisualGraphOptions {
private static final String DEFAULT_GROUP_BACKGROUND_COLOR_DESCRPTION = private static final String DEFAULT_GROUP_BACKGROUND_COLOR_DESCRPTION =
"The default background color applied to newly created group vertices"; "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"; "Update Vertex Colors When Grouping";
private static final String UPDATE_GROUP_AND_UNGROUP_COLORS_DESCRIPTION = 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 " + "Signals that any user color changes to a group vertex will apply that same color to " +
"all grouped vertices as well."; "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; private boolean updateGroupColorsAutomatically = true;
//@formatter:off //@formatter:off
@@ -102,6 +106,7 @@ public class FunctionGraphOptions extends VisualGraphOptions {
private GColor unconditionalJumpEdgeHighlightColor = new GColor("color.bg.plugin.functiongraph.edge.jump.unconditional.highlight"); private GColor unconditionalJumpEdgeHighlightColor = new GColor("color.bg.plugin.functiongraph.edge.jump.unconditional.highlight");
//@formatter:on //@formatter:on
private int maxNodes = 1000;
private boolean useFullSizeTooltip = false; private boolean useFullSizeTooltip = false;
private RelayoutOption relayoutOption = RelayoutOption.VERTEX_GROUPING_CHANGES; private RelayoutOption relayoutOption = RelayoutOption.VERTEX_GROUPING_CHANGES;
@@ -122,6 +127,10 @@ public class FunctionGraphOptions extends VisualGraphOptions {
return updateGroupColorsAutomatically; return updateGroupColorsAutomatically;
} }
public int getMaxNodes() {
return maxNodes;
}
public Color getFallthroughEdgeColor() { public Color getFallthroughEdgeColor() {
return fallthroughEdgeColor; return fallthroughEdgeColor;
} }
@@ -178,12 +187,14 @@ public class FunctionGraphOptions extends VisualGraphOptions {
options.registerThemeColorBinding(DEFAULT_GROUP_BACKGROUND_COLOR_KEY, options.registerThemeColorBinding(DEFAULT_GROUP_BACKGROUND_COLOR_KEY,
defaultGroupBackgroundColor.getId(), help, DEFAULT_GROUP_BACKGROUND_COLOR_DESCRPTION); 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); help, UPDATE_GROUP_AND_UNGROUP_COLORS_DESCRIPTION);
options.registerOption(USE_FULL_SIZE_TOOLTIP_KEY, useFullSizeTooltip, help, options.registerOption(USE_FULL_SIZE_TOOLTIP_KEY, useFullSizeTooltip, help,
USE_FULL_SIZE_TOOLTIP_DESCRIPTION); USE_FULL_SIZE_TOOLTIP_DESCRIPTION);
options.registerOption(MAX_NODES_KEY, maxNodes, help, MAX_NODES_DESCRIPTION);
options.registerThemeColorBinding(EDGE_COLOR_CONDITIONAL_JUMP_KEY, options.registerThemeColorBinding(EDGE_COLOR_CONDITIONAL_JUMP_KEY,
conditionalJumpEdgeColor.getId(), help, "Conditional jump edge color"); 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); useFullSizeTooltip = options.getBoolean(USE_FULL_SIZE_TOOLTIP_KEY, useFullSizeTooltip);
updateGroupColorsAutomatically = 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(); Set<Entry<String, FGLayoutOptions>> entries = layoutOptionsByName.entrySet();
for (Entry<String, FGLayoutOptions> entry : entries) { for (Entry<String, FGLayoutOptions> entry : entries) {
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 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() + "\""); "Finished creating graph for \"" + validatedFunction.getName() + "\"");
} }
catch (CancelledException e) { catch (CancelledException e) {
String message = "Cancelled graph for \"" + validatedFunction.getName() + "\""; String message = "Cancelled graph for \"" + validatedFunction.getName() + "\"";
if (!e.isDefaultMessage()) {
message = e.getMessage();
}
graphData = new EmptyFunctionGraphData(message); graphData = new EmptyFunctionGraphData(message);
monitor.setMessage(message); monitor.setMessage(message);
} }
@@ -1,13 +1,12 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -16,22 +15,30 @@
*/ */
package ghidra.util.exception; package ghidra.util.exception;
/** /**
* <code>CancelledException</code> indicates that the user cancelled * <code>CancelledException</code> indicates that the user cancelled
* the current operation. * the current operation.
*/ */
public class CancelledException extends UsrException { public class CancelledException extends UsrException {
public static final String DEFAULT_MESSAGE = "Operation cancelled";
/** /**
* Default constructor. Message indicates 'Operation cancelled'. * Default constructor. Message indicates 'Operation cancelled'.
*/ */
public CancelledException() { public CancelledException() {
super("Operation cancelled"); super(DEFAULT_MESSAGE);
} }
public CancelledException(String msg) { public CancelledException(String msg) {
super(msg); super(msg);
} }
/**
* {@return true if the message of this exception is {@value #DEFAULT_MESSAGE}}
*/
public boolean isDefaultMessage() {
return DEFAULT_MESSAGE.equals(getMessage());
}
} }