GP-1014 - Function Call Graph - add option to change background color;

consolidated graph options.
This commit is contained in:
dragonmacher
2021-06-30 16:48:51 -04:00
parent 6962885c3e
commit 9112a84f63
22 changed files with 301 additions and 247 deletions

View File

@@ -232,6 +232,15 @@
</P>
</BLOCKQUOTE>
<H2><A name="Options"></A>Options</H2>
<BLOCKQUOTE>
<P>The Function Call Graph options are currently a subset of the
<A href="help/topics/FunctionGraphPlugin/Function_Graph.html#Options">
Function Graph's Options</A>.
</P>
</BLOCKQUOTE>
</BLOCKQUOTE>
@@ -240,8 +249,7 @@
<P class="relatedtopic">Related Topics:</P>
<UL>
<!-- TODO update this when we introduce the generic graphing help -->
<LI>Graphs</LI>
<LI><A href="help/topics/Graph/GraphIntro.html">Graphs</A></LI>
</UL><BR>
<BR>
</BODY>

View File

@@ -25,11 +25,17 @@ import ghidra.graph.viewer.options.VisualGraphOptions;
*/
public class FcgView extends VisualGraphView<FcgVertex, FcgEdge, FunctionCallGraph> {
private VisualGraphOptions options;
public FcgView(VisualGraphOptions options) {
this.options = options;
}
@Override
protected void installGraphViewer() {
FcgComponent component = createGraphComponent();
component.setGraphOptions(new VisualGraphOptions());
component.setGraphOptions(options);
setGraphComponent(component);
}

View File

@@ -15,8 +15,7 @@
*/
package functioncalls.plugin;
import static functioncalls.graph.FcgDirection.IN;
import static functioncalls.graph.FcgDirection.OUT;
import static functioncalls.graph.FcgDirection.*;
import java.awt.*;
import java.awt.event.MouseEvent;
@@ -60,7 +59,7 @@ import resources.Icons;
import util.CollectionUtils;
/**
* The primary component provider for the {@link FunctionCallGraphPlugin}
* The primary component provider for the {@link FunctionCallGraphPlugin}
*/
public class FcgProvider
extends VisualGraphComponentProvider<FcgVertex, FcgEdge, FunctionCallGraph> {
@@ -133,6 +132,10 @@ public class FcgProvider
installGraph();
}
void optionsChanged() {
view.optionsChanged();
}
void locationChanged(ProgramLocation loc) {
if (!navigateIncomingToggleAction.isSelected()) {
return;
@@ -277,7 +280,7 @@ public class FcgProvider
private void buildComponent() {
view = new FcgView();
view = new FcgView(plugin.getOptions());
view.setVertexClickListener((v, info) -> {
@@ -519,7 +522,7 @@ public class FcgProvider
addLocalAction(resetGraphAction);
MultiStateDockingAction<LayoutProvider<FcgVertex, FcgEdge, FunctionCallGraph>> layoutAction =
new MultiStateDockingAction<LayoutProvider<FcgVertex, FcgEdge, FunctionCallGraph>>(
new MultiStateDockingAction<>(
RELAYOUT_GRAPH_ACTION_NAME, plugin.getName()) {
@Override
@@ -627,7 +630,7 @@ public class FcgProvider
//==================================================================================================
// Expand/Collapse Methods
//==================================================================================================
//==================================================================================================
/*
* Notes
@@ -660,8 +663,8 @@ public class FcgProvider
Set<FcgEdge> newEdges = getModelEdges(sources, expandingLevel, edgeNotInGraphFilter);
// Need all vertices from the source level, as well as their edges.
// This is used to correctly layout the vertices we are adding. This way, if we
// Need all vertices from the source level, as well as their edges.
// This is used to correctly layout the vertices we are adding. This way, if we
// later add the sibling vertices, they will be in the correct spot, without clipping.
Iterable<FcgVertex> sourceSiblings = getVerticesByLevel(sourceLevel);
Set<FcgEdge> parentLevelEdges = getModelEdges(sourceSiblings, expandingLevel, unfiltered);
@@ -756,7 +759,7 @@ public class FcgProvider
FcgLevel parentLevel = parent.getLevel();
FcgLevel otherLevel = other.getLevel();
if (!parentLevel.isParentOf(otherLevel)) {
// the other vertex must be in the child level to be a dependent
// the other vertex must be in the child level to be a dependent
return false;
}
@@ -864,7 +867,7 @@ public class FcgProvider
}
private Set<FcgVertex> toStartVertices(Iterable<FcgEdge> edges, Predicate<FcgVertex> filter) {
//@formatter:off
//@formatter:off
return CollectionUtils
.asStream(edges)
.map(e -> e.getStart())
@@ -921,7 +924,7 @@ public class FcgProvider
currentLevel = currentLevel.child();
}
// hand out from greatest to least so that we can close the extremities first
// hand out from greatest to least so that we can close the extremities first
Collections.reverse(result);
@SuppressWarnings("unchecked")
@@ -931,8 +934,8 @@ public class FcgProvider
private void doExpand(FcgExpandingVertexCollection collection) {
// note: we must do this before adding edges, as that will also add vertices and
// we will filter vertices later by those that are not already in the graph
// note: we must do this before adding edges, as that will also add vertices and
// we will filter vertices later by those that are not already in the graph
Set<FcgVertex> newVertices = collection.getNewVertices();
FunctionCallGraph graph = graphData.getGraph();
@@ -979,13 +982,13 @@ public class FcgProvider
/**
* Called when new vertices are added to the graph to ensure that known edges between any
* level of the graph get added as the associated vertices are added to the graph. This
* is needed because we don't add all known edges for a single vertex when it is added, as
* level of the graph get added as the associated vertices are added to the graph. This
* is needed because we don't add all known edges for a single vertex when it is added, as
* its associated vertex may not yet be in the graph. Calling this method ensures that as
* vertices appear, the edges are added.
*
* @param newVertices the vertices being added to the graph
* @param newEdges the set to which should be added any new edges being added to the graph
* @param newEdges the set to which should be added any new edges being added to the graph
*/
private void addEdgesToExistingVertices(Iterable<FcgVertex> newVertices,
Set<FcgEdge> newEdges) {
@@ -1018,7 +1021,7 @@ public class FcgProvider
//
// Unusual Code Alert
// We wish to always use the same vertex *instance* across edges that we are
// We wish to always use the same vertex *instance* across edges that we are
// creating. If the vertex is already in the graph, then that will happen as we get it
// from the graph. However, if the function does not have a vertex in the graph, then
// it must be created. Cache the vertices we retrieve here, whether exiting or created,
@@ -1026,7 +1029,7 @@ public class FcgProvider
//
//@formatter:off
return CollectionUtils.asStream(callees)
return CollectionUtils.asStream(callees)
.map(f -> {
if (newVertexCache.containsKey(f)) {

View File

@@ -17,18 +17,21 @@ package functioncalls.plugin;
import docking.ActionContext;
import docking.action.DockingAction;
import docking.tool.ToolConstants;
import ghidra.app.CorePluginPackage;
import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.plugin.ProgramPlugin;
import ghidra.app.services.GoToService;
import ghidra.framework.options.SaveState;
import ghidra.framework.options.*;
import ghidra.framework.plugintool.PluginInfo;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.util.PluginStatus;
import ghidra.graph.viewer.options.VisualGraphOptions;
import ghidra.program.model.address.Address;
import ghidra.program.util.ProgramLocation;
import ghidra.util.HelpLocation;
import ghidra.util.SystemUtilities;
import ghidra.util.bean.opteditor.OptionsVetoException;
import ghidra.util.task.SwingUpdateManager;
/**
@@ -43,7 +46,7 @@ import ghidra.util.task.SwingUpdateManager;
description = "Displays a graph of incoming and outgoing calls for a given function."
)
//@formatter:on
public class FunctionCallGraphPlugin extends ProgramPlugin {
public class FunctionCallGraphPlugin extends ProgramPlugin implements OptionsChangeListener {
/*package*/ static final String NAME = "Function Call Graph";
/*package*/ static final String SHOW_PROVIDER_ACTION_NAME = "Display Function Call Graph";
@@ -52,6 +55,7 @@ public class FunctionCallGraphPlugin extends ProgramPlugin {
FunctionCallGraphPlugin.class.getSimpleName());
private FcgProvider provider;
private VisualGraphOptions vgOptions = new VisualGraphOptions();
// enough time for users to click around without the graph starting its work
private static final int MIN_UPDATE_DELAY = 750;
@@ -65,8 +69,32 @@ public class FunctionCallGraphPlugin extends ProgramPlugin {
@Override
protected void init() {
provider = new FcgProvider(tool, this);
createActions();
initializeOptions();
}
private void initializeOptions() {
ToolOptions options = tool.getOptions(ToolConstants.GRAPH_OPTIONS);
options.addOptionsChangeListener(this);
HelpLocation help = new HelpLocation(getName(), "Options");
Options callGraphOptions = options.getOptions(NAME);
vgOptions.registerOptions(callGraphOptions, help);
vgOptions.loadOptions(callGraphOptions);
provider.optionsChanged();
}
@Override
public void optionsChanged(ToolOptions options, String optionName, Object oldValue,
Object newValue) throws OptionsVetoException {
Options callGraphOptions = options.getOptions(NAME);
vgOptions.loadOptions(callGraphOptions);
provider.optionsChanged();
}
@Override
@@ -118,7 +146,7 @@ public class FunctionCallGraphPlugin extends ProgramPlugin {
}
};
// TODO create icon from scratch: bow-tie
// TODO create icon from scratch: bow-tie
// ImageIcon icon = ResourceManager.loadImage("images/applications-development.png");
// showProviderAction.setToolBarData(new ToolBarData(icon, "View"));
tool.addAction(showProviderAction);
@@ -142,4 +170,8 @@ public class FunctionCallGraphPlugin extends ProgramPlugin {
ProgramLocation getCurrentLocation() {
return currentLocation;
}
VisualGraphOptions getOptions() {
return vgOptions;
}
}