Changes Program Graph actions to not graph entire program when there is no selection

This commit is contained in:
ghidravore 2020-11-03 13:43:03 -05:00
parent 5b8be18b40
commit c02ab0160c
4 changed files with 135 additions and 65 deletions

View file

@ -45,11 +45,41 @@
systems that have convoluted control flow structure, it may be beneficial to graph the entire
program.</P>
</BLOCKQUOTE>
<H2>Graph Scope</H2>
<P>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. </P>
<BLOCKQUOTE>
<H3>If there is a selection:</H3>
<BLOCKQUOTE><P> The graph will
include all the code that is selected and exclude all the code that is not selected.</P>
</BLOCKQUOTE>
<H3>If there is no selection:</H3>
<BLOCKQUOTE>
<P> The scope is determined by the current cursor location and the type of graph:</P>
</BLOCKQUOTE>
<BLOCKQUOTE>
<H4>Current Location is in a Function</H4>
<UL>
<LI>Block Flow Graphs: The scope is the body of the containing functions</LI>
<LI>Call Graphs: The scope is the containing function and all the functions that
either call it or it calls</LI>
</UL>
<H4>Current Location is not in a Function</H4>
<UL>
<LI>the scope will be the entire program</LI>
</UL>
</BLOCKQUOTE>
<P><IMG src="../../shared/tip.png"> To graph the entire program, press "&lt;ctrl&gt; a" to
select all before creating the graph.</P>
</BLOCKQUOTE>
<H2>Synchronization</H2>
<P>Selection and Location events are synchronized between each
graph and the other windows in the tool.
<H2>Selection</H2>
<BLOCKQUOTE>
<H3>Selection</H3>
<P>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.</P>
</BLOCKQUOTE>
<H2>Location</H2>
<H3>Location</H3>
<P>The node containing the current address location is marked with a large red arrow as shown
below on the graph node labeled "00408133".</P>
@ -94,7 +124,7 @@
<P align="left">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.</P>
</BLOCKQUOTE>
<H2>Graph Representation</H2>
<P>By Default, the graphs use the following icons and colors to represent the nodes and edges.</P>

View file

@ -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;
}

View file

@ -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() {