diff --git a/Ghidra/Features/ProgramGraph/src/main/help/help/topics/ProgramGraphPlugin/ProgramGraph.htm b/Ghidra/Features/ProgramGraph/src/main/help/help/topics/ProgramGraphPlugin/ProgramGraph.htm index 0cb44ad2d6..a67e138644 100644 --- a/Ghidra/Features/ProgramGraph/src/main/help/help/topics/ProgramGraphPlugin/ProgramGraph.htm +++ b/Ghidra/Features/ProgramGraph/src/main/help/help/topics/ProgramGraphPlugin/ProgramGraph.htm @@ -45,11 +45,41 @@ systems that have convoluted control flow structure, it may be beneficial to graph the entire program.

+ +

Graph Scope

+

When creating any of the graphs described below, the scope of the graph is determined by the + current location and selection present in the listing.

+
+

If there is a selection:

+

The graph will + include all the code that is selected and exclude all the code that is not selected.

+
+

If there is no selection:

+
+

The scope is determined by the current cursor location and the type of graph:

+
+
+

Current Location is in a Function

+ +

Current Location is not in a Function

+ +
+

To graph the entire program, press "<ctrl> a" to + select all before creating the graph.

+
+ +

Synchronization

Selection and Location events are synchronized between each graph and the other windows in the tool. - -

Selection

+
+

Selection

The current selection within the graph display is represented by a red box around selected nodes as shown below on the node labeled "00408133". A node is selected if any addresses it represents are contained within the @@ -74,7 +104,7 @@ from the basic blocks found within the selected subroutine.

-

Location

+

Location

The node containing the current address location is marked with a large red arrow as shown below on the graph node labeled "00408133".

@@ -94,7 +124,7 @@

Clicking on a node in the graph display causes the current address location within Ghidra to change to the minimum address represented by the graph node.

- +

Graph Representation

By Default, the graphs use the following icons and colors to represent the nodes and edges.

diff --git a/Ghidra/Features/ProgramGraph/src/main/java/ghidra/graph/program/BlockGraphTask.java b/Ghidra/Features/ProgramGraph/src/main/java/ghidra/graph/program/BlockGraphTask.java index 96b7215070..7fda8e3bd5 100644 --- a/Ghidra/Features/ProgramGraph/src/main/java/ghidra/graph/program/BlockGraphTask.java +++ b/Ghidra/Features/ProgramGraph/src/main/java/ghidra/graph/program/BlockGraphTask.java @@ -21,8 +21,7 @@ import java.util.*; import docking.widgets.EventTrigger; import ghidra.app.plugin.core.colorizer.ColorizingService; import ghidra.framework.plugintool.PluginTool; -import ghidra.program.model.address.Address; -import ghidra.program.model.address.AddressSetView; +import ghidra.program.model.address.*; import ghidra.program.model.block.*; import ghidra.program.model.listing.*; import ghidra.program.model.symbol.*; @@ -110,8 +109,9 @@ public class BlockGraphTask extends Task { private boolean reuseGraph; private boolean appendGraph; private PluginTool tool; - private String actionName; private Program program; + private AddressSetView graphScope; + private String graphTitle; public BlockGraphTask(String actionName, boolean graphEntryPointNexus, boolean showCode, boolean reuseGraph, boolean appendGraph, PluginTool tool, ProgramSelection selection, @@ -119,8 +119,6 @@ public class BlockGraphTask extends Task { GraphDisplayProvider graphProvider) { super("Graph Program", true, false, true); - this.actionName = actionName; - this.graphEntryPointNexus = graphEntryPointNexus; this.showCode = showCode; this.reuseGraph = reuseGraph; @@ -132,6 +130,7 @@ public class BlockGraphTask extends Task { this.selection = selection; this.location = location; this.program = blockModel.getProgram(); + this.graphTitle = actionName + ": "; } /** @@ -139,6 +138,7 @@ public class BlockGraphTask extends Task { */ @Override public void run(TaskMonitor monitor) throws CancelledException { + this.graphScope = getGraphScopeAndGenerateGraphTitle(); AttributedGraph graph = createGraph(); monitor.setMessage("Generating Graph..."); try { @@ -153,7 +153,7 @@ public class BlockGraphTask extends Task { display.setVertexLabel(CODE_ATTRIBUTE, GraphDisplay.ALIGN_LEFT, 12, true, codeLimitPerBlock + 1); } - display.setGraph(graph, getDescription(), appendGraph, monitor); + display.setGraph(graph, graphTitle, appendGraph, monitor); if (location != null) { // initialize the graph location, but don't have the graph send an event @@ -175,17 +175,6 @@ public class BlockGraphTask extends Task { } } - private String getDescription() { - String description = actionName; - if (selection != null && !selection.isEmpty()) { - description += ": " + selection.getMinAddress(); - } - else { - description += " (Entire Program)"; - } - return description; - } - /** * Set the maximum number of code lines which will be used per block when * showCode is enabled. @@ -221,10 +210,62 @@ public class BlockGraphTask extends Task { } private CodeBlockIterator getBlockIterator() throws CancelledException { - if (selection == null || selection.isEmpty()) { - return blockModel.getCodeBlocks(taskMonitor); + return blockModel.getCodeBlocksContaining(graphScope, taskMonitor); + } + + private AddressSetView getGraphScopeAndGenerateGraphTitle() { + if (selection != null && !selection.isEmpty()) { + graphTitle += selection.getMinAddress().toString(); + return selection; } - return blockModel.getCodeBlocksContaining(selection, taskMonitor); + Function function = getContainingFunction(location); + if (function != null) { + graphTitle += function.getName(); + if (isCallGraph()) { + return getScopeForCallGraph(function); + } + return function.getBody(); + } + graphTitle += "(Entire Program)"; + return blockModel.getProgram().getMemory(); + } + + private boolean isCallGraph() { + return blockModel instanceof SubroutineBlockModel; + } + + private AddressSetView getScopeForCallGraph(Function function) { + AddressSet set = new AddressSet(); + set.add(function.getBody()); + try { + CodeBlock block = blockModel.getCodeBlockAt(function.getEntryPoint(), taskMonitor); + CodeBlockReferenceIterator it = blockModel.getDestinations(block, taskMonitor); + while (it.hasNext()) { + CodeBlockReference next = it.next(); + set.add(next.getDestinationBlock()); + } + it = blockModel.getSources(block, taskMonitor); + while (it.hasNext()) { + CodeBlockReference next = it.next(); + set.add(next.getSourceBlock()); + } + } + catch (CancelledException e) { + // just return, the task is being cancelled. + } + + return set; + } + + private Function getContainingFunction(ProgramLocation cursorLocation) { + if (cursorLocation == null) { + return null; + } + Address address = cursorLocation.getAddress(); + if (address == null) { + return null; + } + return blockModel.getProgram().getFunctionManager().getFunctionContaining(address); } private Address graphBlock(AttributedGraph graph, CodeBlock curBB, @@ -285,7 +326,7 @@ public class BlockGraphTask extends Task { // don't include destination if it does not overlap selection // always include if selection is empty - if (selection != null && !selection.isEmpty() && !selection.intersects(db)) { + if (graphScope != null && !graphScope.isEmpty() && !graphScope.intersects(db)) { continue; } diff --git a/Ghidra/Features/ProgramGraph/src/main/java/ghidra/graph/program/ProgramGraphPlugin.java b/Ghidra/Features/ProgramGraph/src/main/java/ghidra/graph/program/ProgramGraphPlugin.java index 0279faa16f..8a687cda9d 100644 --- a/Ghidra/Features/ProgramGraph/src/main/java/ghidra/graph/program/ProgramGraphPlugin.java +++ b/Ghidra/Features/ProgramGraph/src/main/java/ghidra/graph/program/ProgramGraphPlugin.java @@ -177,49 +177,49 @@ public class ProgramGraphPlugin extends ProgramPlugin private void createActions() { new ActionBuilder("Graph Block Flow", getName()) - .menuPath(MENU_GRAPH, "&Block Flow") - .menuGroup("Graph", "A") - .onAction(c -> graphBlockFlow()) - .enabledWhen(this::canGraph) - .buildAndInstall(tool); + .menuPath(MENU_GRAPH, "&Block Flow") + .menuGroup("Graph", "A") + .onAction(c -> graphBlockFlow()) + .enabledWhen(this::canGraph) + .buildAndInstall(tool); new ActionBuilder("Graph Code Flow", getName()) - .menuPath(MENU_GRAPH, "C&ode Flow") - .menuGroup("Graph", "B") - .onAction(c -> graphCodeFlow()) - .enabledWhen(this::canGraph) - .buildAndInstall(tool); + .menuPath(MENU_GRAPH, "C&ode Flow") + .menuGroup("Graph", "B") + .onAction(c -> graphCodeFlow()) + .enabledWhen(this::canGraph) + .buildAndInstall(tool); new ActionBuilder("Graph Calls Using Default Model", getName()) - .menuPath(MENU_GRAPH, "&Calls") - .menuGroup("Graph", "C") - .onAction(c -> graphSubroutines()) - .enabledWhen(this::canGraph) - .buildAndInstall(tool); + .menuPath(MENU_GRAPH, "&Calls") + .menuGroup("Graph", "C") + .onAction(c -> graphSubroutines()) + .enabledWhen(this::canGraph) + .buildAndInstall(tool); reuseGraphAction = new ToggleActionBuilder("Reuse Graph", getName()) - .menuPath(MENU_GRAPH, "Reuse Graph") - .menuGroup("Graph Options") - .selected(reuseGraph) - .onAction(c -> reuseGraph = reuseGraphAction.isSelected()) - .enabledWhen(this::canGraph) - .buildAndInstall(tool); + .menuPath(MENU_GRAPH, "Reuse Graph") + .menuGroup("Graph Options") + .selected(reuseGraph) + .onAction(c -> reuseGraph = reuseGraphAction.isSelected()) + .enabledWhen(this::canGraph) + .buildAndInstall(tool); appendGraphAction = new ToggleActionBuilder("Append Graph", getName()) - .menuPath(MENU_GRAPH, "Append Graph") - .menuGroup("Graph Options") - .selected(false) - .onAction(c -> updateAppendAndReuseGraph()) - .enabledWhen(this::canGraph) - .buildAndInstall(tool); + .menuPath(MENU_GRAPH, "Append Graph") + .menuGroup("Graph Options") + .selected(false) + .onAction(c -> updateAppendAndReuseGraph()) + .enabledWhen(this::canGraph) + .buildAndInstall(tool); forceLocationVisibleAction = new ToggleActionBuilder("Show Location in Graph", getName()) - .menuPath(MENU_GRAPH, "Show Location") - .description("Tell the graph to pan/scale as need to keep location changes visible") - .menuGroup("Graph Options") - .onAction(c -> toggleForceLocationVisible()) - .enabledWhen(this::canGraph) - .buildAndInstall(tool); + .menuPath(MENU_GRAPH, "Show Location") + .description("Tell the graph to pan/scale as need to keep location changes visible") + .menuGroup("Graph Options") + .onAction(c -> toggleForceLocationVisible()) + .enabledWhen(this::canGraph) + .buildAndInstall(tool); updateSubroutineActions(); } @@ -268,12 +268,12 @@ public class ProgramGraphPlugin extends ProgramPlugin private DockingAction buildGraphActionWithModel(String blockModelName, HelpLocation helpLoc) { return new ActionBuilder("Graph Calls using " + blockModelName, getName()) - .menuPath("Graph", "Calls Using Model", blockModelName) - .menuGroup("Graph") - .helpLocation(helpLoc) - .onAction(c -> graphSubroutinesUsing(blockModelName)) - .enabledWhen(this::canGraph) - .buildAndInstall(tool); + .menuPath("Graph", "Calls Using Model", blockModelName) + .menuGroup("Graph") + .helpLocation(helpLoc) + .onAction(c -> graphSubroutinesUsing(blockModelName)) + .enabledWhen(this::canGraph) + .buildAndInstall(tool); } private void graphBlockFlow() { diff --git a/Ghidra/Features/ProgramGraph/src/test/java/ghidra/graph/program/BlockGraphEventTest.java b/Ghidra/Features/ProgramGraph/src/test/java/ghidra/graph/program/BlockGraphEventTest.java index f7135cdf5e..c7c73dde05 100644 --- a/Ghidra/Features/ProgramGraph/src/test/java/ghidra/graph/program/BlockGraphEventTest.java +++ b/Ghidra/Features/ProgramGraph/src/test/java/ghidra/graph/program/BlockGraphEventTest.java @@ -65,7 +65,6 @@ public class BlockGraphEventTest extends AbstractBlockGraphTest { assertEquals("01002239", display.getFocusedVertex().getId()); } - private AddressSet addrSet(long start, long end) { return new AddressSet(addr(start), addr(end)); }