mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 17:59:46 +02:00
Merge branch 'GP-194_ghidra42_data_reference_graph'
Conflicts: Ghidra/Features/ProgramGraph/src/test/java/ghidra/graph/program/TestGraphDisplay.java
This commit is contained in:
commit
d3e7902a8b
10 changed files with 1175 additions and 56 deletions
|
@ -41,8 +41,7 @@ abstract class ProgramGraphFunctions {
|
||||||
/**
|
/**
|
||||||
* a default implementation of a {@link ShapeFactory} to supply shapes for attributed vertices and edges
|
* a default implementation of a {@link ShapeFactory} to supply shapes for attributed vertices and edges
|
||||||
*/
|
*/
|
||||||
private static ShapeFactory<Attributed> shapeFactory =
|
private static ShapeFactory<Attributed> shapeFactory = new ShapeFactory<>(n -> 50, n -> 1.0f);
|
||||||
new ShapeFactory<>(n -> 50, n -> 1.0f);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* return various 'Shapes' based on an attribute name
|
* return various 'Shapes' based on an attribute name
|
||||||
|
@ -79,34 +78,36 @@ abstract class ProgramGraphFunctions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Gets the Shape object to use when drawing this vertex. If "Icon" attribute
|
||||||
|
* is set it will use that, otherwise "VertexType" to will translate a code flow
|
||||||
|
* name to a shape
|
||||||
|
*
|
||||||
|
* @param vertex the Attributed object to get a shape for
|
||||||
|
* @return a Shape object to use when displaying the object
|
||||||
|
*/
|
||||||
public static Shape getVertexShape(Attributed vertex) {
|
public static Shape getVertexShape(Attributed vertex) {
|
||||||
try {
|
Shape shape = byShapeName(vertex, vertex.getAttribute("Icon"));
|
||||||
String vertexType = vertex.getAttribute("VertexType");
|
if (shape != null) {
|
||||||
Shape shape = byShapeName(vertex, vertex.getAttribute("Icon"));
|
return shape;
|
||||||
if (shape != null) {
|
|
||||||
return shape;
|
|
||||||
}
|
|
||||||
if (vertexType == null) {
|
|
||||||
return shapeFactory.getRectangle(vertex);
|
|
||||||
}
|
|
||||||
switch (vertexType) {
|
|
||||||
case "Entry":
|
|
||||||
return shapeFactory.getRegularPolygon(vertex, 3, Math.PI);
|
|
||||||
case "Exit":
|
|
||||||
return shapeFactory.getRegularPolygon(vertex, 3);
|
|
||||||
case "Switch":
|
|
||||||
return shapeFactory.getRectangle(vertex, Math.PI / 4);
|
|
||||||
case "Body":
|
|
||||||
case "External":
|
|
||||||
return shapeFactory.getRectangle(vertex);
|
|
||||||
default:
|
|
||||||
return shapeFactory.getEllipse(vertex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception ex) {
|
String vertexType = vertex.getAttribute("VertexType");
|
||||||
// just return a rectangle
|
if (vertexType == null) {
|
||||||
return shapeFactory.getRectangle(vertex);
|
return shapeFactory.getRectangle(vertex);
|
||||||
}
|
}
|
||||||
|
switch (vertexType) {
|
||||||
|
case "Entry":
|
||||||
|
return shapeFactory.getRegularPolygon(vertex, 3, Math.PI);
|
||||||
|
case "Exit":
|
||||||
|
return shapeFactory.getRegularPolygon(vertex, 3);
|
||||||
|
case "Switch":
|
||||||
|
return shapeFactory.getRectangle(vertex, Math.PI / 4);
|
||||||
|
case "Body":
|
||||||
|
case "External":
|
||||||
|
return shapeFactory.getRectangle(vertex);
|
||||||
|
default:
|
||||||
|
return shapeFactory.getEllipse(vertex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -27,10 +27,16 @@
|
||||||
another. The nodes of the graph represent blocks of code and the edges represent flow between
|
another. The nodes of the graph represent blocks of code and the edges represent flow between
|
||||||
the blocks of code.</P>
|
the blocks of code.</P>
|
||||||
|
|
||||||
<P>There are two basic graph types, <A href="#Graph_Block_Flow">Block Flow</A> and <A href=
|
<P>There are two basic flow graph types, <A href="#Graph_Block_Flow">Block Flow</A> and <A href=
|
||||||
"#Graph_Calls_Using_Default_Model">Call Graph</A>. Different colors and shapes are used to
|
"#Graph_Calls_Using_Default_Model">Call Graph</A>. Different colors and shapes are used to
|
||||||
depict each node and the flow between them. Multiple graphs of either type can exist at any
|
depict each node and the flow between them.</P>
|
||||||
time.</P>
|
|
||||||
|
<P><A href="#Data_Reference_Graph">Data graphs</A> show reference relationships between memory
|
||||||
|
locations. They can be constrained to just <B>"To References"</B>, just <B>"From References"</B>,
|
||||||
|
or both. By default a single hop is graphed with the option to extend from within the
|
||||||
|
graph.</P>
|
||||||
|
|
||||||
|
<P>Multiple graphs of any or all types can exist at any time.</P>
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<P>A <I>Call Graph</I> depicts subroutines as nodes. Calls between subroutines are shown as
|
<P>A <I>Call Graph</I> depicts subroutines as nodes. Calls between subroutines are shown as
|
||||||
|
@ -434,6 +440,55 @@
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
<H2><A name="Data_Reference_Graph"></A>Graph Data References</H2>
|
||||||
|
<P>A graph of data references can be created with <B>Graph<IMG src="../../shared/arrow.gif">
|
||||||
|
Data</B> then selecting <B>To References</B>, <B>From References</B> or <B>To/From References</B>.
|
||||||
|
Only the selected references will be included. By default only a single layer of references will
|
||||||
|
be graphed, this can be adjusted using the <B>Edit <IMG src="../../shared/arrow.gif">
|
||||||
|
Tool Options <IMG src="../../shared/arrow.gif"> Graph <IMG src="../../shared/arrow.gif">
|
||||||
|
Max Reference Depth</B>
|
||||||
|
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<P><IMG src="../../shared/note.png"> Unlike flow graphs, where only the selection is
|
||||||
|
included in the graph, the selection and its references are included.</P>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
<P>To graph Data References,</P>
|
||||||
|
|
||||||
|
<OL>
|
||||||
|
<LI>Select the data to start from in the listing
|
||||||
|
<LI>Select <B>Graph<IMG src="../../shared/arrow.gif"> Data<IMG src=
|
||||||
|
"../../shared/arrow.gif"></B></LI>
|
||||||
|
<LI>
|
||||||
|
Select one of
|
||||||
|
|
||||||
|
<UL>
|
||||||
|
<LI>To References - selected data and all references to it</LI>
|
||||||
|
|
||||||
|
<LI>From References - selected data all references from it</LI>
|
||||||
|
|
||||||
|
<LI>To/From References - selected data and all references to and from it</LI>
|
||||||
|
</UL>
|
||||||
|
</LI>
|
||||||
|
|
||||||
|
<LI>A new graph window is created with the selected data and the specified references.
|
||||||
|
By default this includes one layer of references but this may be adjusted in the options
|
||||||
|
as specified above, or dynamically using the <A href="#Add_References">Add References
|
||||||
|
action</A> on the desired node</LI>
|
||||||
|
</OL>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
|
||||||
|
<H3><A NAME="Add_References"></A>Add References</H3>
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<P>Allows the user to add references starting from
|
||||||
|
the selected nodes, with To References, From References or Bidirectional as selected.
|
||||||
|
</P>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
<BR>
|
<BR>
|
||||||
<BR>
|
<BR>
|
||||||
|
|
|
@ -0,0 +1,214 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.graph.program;
|
||||||
|
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.listing.*;
|
||||||
|
import ghidra.program.model.symbol.Reference;
|
||||||
|
import ghidra.service.graph.*;
|
||||||
|
import ghidra.util.exception.CancelledException;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A graph meant to hold data reference information
|
||||||
|
* <p>
|
||||||
|
* Recursively adds references from a specified address to a specified number
|
||||||
|
* of hops. Displays the reference type and source as attributes. Supports graph
|
||||||
|
* extension in place.
|
||||||
|
*/
|
||||||
|
public class DataReferenceGraph extends AttributedGraph {
|
||||||
|
public static final String REF_SOURCE_ATTRIBUTE = "Source";
|
||||||
|
public static final String REF_TYPE_ATTRIBUTE = "Type";
|
||||||
|
public static final String REF_SYMBOL_ATTRIBUTE = "Symbol";
|
||||||
|
public static final String DATA_ATTRIBUTE = "Data Type";
|
||||||
|
public static final String ADDRESS_ATTRIBUTE = "Address";
|
||||||
|
public static final String LABEL_ATTRIBUTE = "Label";
|
||||||
|
|
||||||
|
public enum Directions {
|
||||||
|
TO_ONLY, FROM_ONLY, BOTH_WAYS
|
||||||
|
}
|
||||||
|
|
||||||
|
private Program program;
|
||||||
|
private int depthPerStep;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param program the Program to pull references from
|
||||||
|
* @param depth the number of hops to graph per call (0 for recursion until no more hops)
|
||||||
|
*/
|
||||||
|
public DataReferenceGraph(Program program, int depth) {
|
||||||
|
this.program = program;
|
||||||
|
this.depthPerStep = depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Constructs the name of the Vertex for the specified address. All addresses in the same CodeUnit
|
||||||
|
* will use the same name/Vertex. If a symbol is available it will use that, otherwise the
|
||||||
|
* string of the address
|
||||||
|
*
|
||||||
|
* @param address address to create the name for
|
||||||
|
* @return String to be used as a VertexId
|
||||||
|
*/
|
||||||
|
public String makeName(Address address) {
|
||||||
|
CodeUnit unit = program.getListing().getCodeUnitContaining(address);
|
||||||
|
if (unit == null) {
|
||||||
|
return address.toString();
|
||||||
|
}
|
||||||
|
Address unitAddress = unit.getAddress();
|
||||||
|
|
||||||
|
String name;
|
||||||
|
if (program.getSymbolTable().getPrimarySymbol(unitAddress) != null) {
|
||||||
|
name = program.getSymbolTable().getPrimarySymbol(unitAddress).getName(true);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
name = unitAddress.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Graphs the references starting at a specified address. If the specified address
|
||||||
|
* doesn't lead to any references the graph will contain only that address.
|
||||||
|
*
|
||||||
|
* @param baseAddress Address to start graphing from
|
||||||
|
* @param direction controls whether to, from, or both references are followed
|
||||||
|
* @param monitor monitor for cancellation
|
||||||
|
*/
|
||||||
|
public AttributedVertex graphFrom(Address baseAddress, Directions direction,
|
||||||
|
TaskMonitor monitor) throws CancelledException {
|
||||||
|
if (baseAddress == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
AttributedVertex baseVertex = new AttributedVertex(makeName(baseAddress));
|
||||||
|
baseVertex.setAttribute(ADDRESS_ATTRIBUTE, baseAddress.toString());
|
||||||
|
setupVertex(baseVertex);
|
||||||
|
addVertex(baseVertex);
|
||||||
|
recurseGraph(baseAddress, depthPerStep, direction, monitor);
|
||||||
|
return baseVertex;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupEdge(AttributedEdge edge, Reference ref) {
|
||||||
|
edge.setAttribute(REF_SOURCE_ATTRIBUTE, ref.getSource().getDisplayString());
|
||||||
|
edge.setAttribute(REF_TYPE_ATTRIBUTE, ref.getReferenceType().toString());
|
||||||
|
if (ref.getSymbolID() != -1) {
|
||||||
|
edge.setAttribute(REF_SYMBOL_ATTRIBUTE,
|
||||||
|
program.getSymbolTable().getSymbol(ref.getSymbolID()).getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupVertex(AttributedVertex vertex) {
|
||||||
|
Address address =
|
||||||
|
program.getAddressFactory().getAddress(vertex.getAttribute(ADDRESS_ATTRIBUTE));
|
||||||
|
if (address == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CodeUnit unit = program.getListing().getCodeUnitContaining(address);
|
||||||
|
if (unit instanceof Data) {
|
||||||
|
vertex.setAttribute(DATA_ATTRIBUTE, ((Data) unit).getBaseDataType().getName());
|
||||||
|
}
|
||||||
|
else if (unit instanceof Instruction) {
|
||||||
|
vertex.setAttribute("Icon", "TriangleDown");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* recursion function, maxDepth of zero indicates go to end
|
||||||
|
*/
|
||||||
|
private void recurseGraph(Address startAddress, int maxDepth, Directions direction,
|
||||||
|
TaskMonitor monitor) throws CancelledException {
|
||||||
|
AttributedVertex startVertex = getVertex(makeName(startAddress));
|
||||||
|
|
||||||
|
if (direction != Directions.FROM_ONLY) {
|
||||||
|
for (Reference ref : program.getListing()
|
||||||
|
.getCodeUnitContaining(startAddress)
|
||||||
|
.getReferenceIteratorTo()) {
|
||||||
|
if (!ref.getReferenceType().isFlow()) {
|
||||||
|
Address nextAddress = processReference(Directions.TO_ONLY, startVertex, ref);
|
||||||
|
monitor.checkCanceled();
|
||||||
|
if (nextAddress != null) {
|
||||||
|
/*
|
||||||
|
* maxDepth > 1 -> subtract 1 to count this level, and keep going
|
||||||
|
* maxDepth = 0 -> no limit, always keep going
|
||||||
|
* maxDepth = 1 -> This is the last one, stop recursion
|
||||||
|
*/
|
||||||
|
if (maxDepth > 1) {
|
||||||
|
recurseGraph(nextAddress, maxDepth - 1, direction, monitor);
|
||||||
|
}
|
||||||
|
else if (maxDepth == 0) {
|
||||||
|
recurseGraph(nextAddress, 0, direction, monitor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (direction != Directions.TO_ONLY) {
|
||||||
|
for (Reference ref : program.getListing()
|
||||||
|
.getCodeUnitContaining(startAddress)
|
||||||
|
.getReferencesFrom()) {
|
||||||
|
if (!ref.getReferenceType().isFlow()) {
|
||||||
|
Address nextAddress = processReference(Directions.FROM_ONLY, startVertex, ref);
|
||||||
|
monitor.checkCanceled();
|
||||||
|
if (nextAddress != null) {
|
||||||
|
/*
|
||||||
|
* maxDepth > 1 -> subtract 1 to count this level, and keep going
|
||||||
|
* maxDepth = 0 -> no limit, always keep going
|
||||||
|
* maxDepth = 1 -> This is the last one, stop recursion
|
||||||
|
*/
|
||||||
|
if (maxDepth > 1) {
|
||||||
|
recurseGraph(nextAddress, maxDepth - 1, direction, monitor);
|
||||||
|
}
|
||||||
|
else if (maxDepth == 0) {
|
||||||
|
recurseGraph(nextAddress, 0, direction, monitor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Address processReference(Directions direction, AttributedVertex startVertex,
|
||||||
|
Reference ref) {
|
||||||
|
Address targetAddress;
|
||||||
|
if (direction == Directions.TO_ONLY) {
|
||||||
|
targetAddress = ref.getFromAddress();
|
||||||
|
}
|
||||||
|
else { //should be FROM_ONLY
|
||||||
|
targetAddress = ref.getToAddress();
|
||||||
|
}
|
||||||
|
AttributedVertex newVertex = new AttributedVertex(makeName(targetAddress));
|
||||||
|
newVertex.setAttribute(ADDRESS_ATTRIBUTE, targetAddress.toString());
|
||||||
|
setupVertex(newVertex);
|
||||||
|
AttributedEdge edge;
|
||||||
|
if (direction == Directions.TO_ONLY) {
|
||||||
|
edge = addEdge(newVertex, startVertex);
|
||||||
|
}
|
||||||
|
else { //should be FROM_ONLY
|
||||||
|
edge = addEdge(startVertex, newVertex);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* if we've seen this before don't do it again
|
||||||
|
*/
|
||||||
|
if (edge.hasAttribute("Weight")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
setupEdge(edge, ref);
|
||||||
|
return targetAddress;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,140 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.graph.program;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import docking.action.builder.ActionBuilder;
|
||||||
|
import docking.widgets.EventTrigger;
|
||||||
|
import ghidra.app.plugin.core.graph.AddressBasedGraphDisplayListener;
|
||||||
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
|
import ghidra.program.model.address.*;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
import ghidra.service.graph.*;
|
||||||
|
import ghidra.util.HelpLocation;
|
||||||
|
import ghidra.util.task.TaskLauncher;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Listener for a GraphDisplay holding a Reference graph. Allows for the extension of the
|
||||||
|
* graph from a specified node
|
||||||
|
*/
|
||||||
|
public class DataReferenceGraphDisplayListener extends AddressBasedGraphDisplayListener {
|
||||||
|
|
||||||
|
private int stepDepth;
|
||||||
|
|
||||||
|
public DataReferenceGraphDisplayListener(PluginTool tool, GraphDisplay display, Program program,
|
||||||
|
int depth) {
|
||||||
|
super(tool, program, display);
|
||||||
|
|
||||||
|
stepDepth = depth;
|
||||||
|
|
||||||
|
HelpLocation helpLoc = new HelpLocation("ProgramGraphPlugin", "Add_References");
|
||||||
|
display.addAction(new ActionBuilder("Add To/From References", "Data Graph")
|
||||||
|
.popupMenuPath("Add Bidirectional References For Selection")
|
||||||
|
.withContext(VertexGraphActionContext.class)
|
||||||
|
.helpLocation(helpLoc)
|
||||||
|
.onAction(this::addToGraph)
|
||||||
|
.build());
|
||||||
|
display.addAction(new ActionBuilder("Add To References", "Data Graph")
|
||||||
|
.popupMenuPath("Add References To Selection")
|
||||||
|
.withContext(VertexGraphActionContext.class)
|
||||||
|
.helpLocation(helpLoc)
|
||||||
|
.onAction(this::addTosToGraph)
|
||||||
|
.build());
|
||||||
|
display.addAction(new ActionBuilder("Add From References", "Data Graph")
|
||||||
|
.popupMenuPath("Add References From Selection")
|
||||||
|
.withContext(VertexGraphActionContext.class)
|
||||||
|
.helpLocation(helpLoc)
|
||||||
|
.onAction(this::addFromsToGraph)
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addToGraph(VertexGraphActionContext context) {
|
||||||
|
doAdd(context, DataReferenceGraph.Directions.BOTH_WAYS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addTosToGraph(VertexGraphActionContext context) {
|
||||||
|
doAdd(context, DataReferenceGraph.Directions.TO_ONLY);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addFromsToGraph(VertexGraphActionContext context) {
|
||||||
|
doAdd(context, DataReferenceGraph.Directions.FROM_ONLY);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doAdd(VertexGraphActionContext context, DataReferenceGraph.Directions direction) {
|
||||||
|
AddressSet addresses = new AddressSet();
|
||||||
|
for (AttributedVertex vertex : context.getSelectedVertices()) {
|
||||||
|
addresses.add(getAddress(vertex));
|
||||||
|
}
|
||||||
|
DataReferenceGraphTask task = new DataReferenceGraphTask(tool, program, addresses,
|
||||||
|
graphDisplay, stepDepth, direction);
|
||||||
|
new TaskLauncher(task, tool.getToolFrame());
|
||||||
|
|
||||||
|
/* I don't know why the selection was going all wonky, but reset it */
|
||||||
|
graphDisplay.selectVertices(context.getSelectedVertices(), EventTrigger.INTERNAL_ONLY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GraphDisplayListener cloneWith(GraphDisplay newDisplay) {
|
||||||
|
return new DataReferenceGraphDisplayListener(tool, newDisplay, program, stepDepth);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Set<AttributedVertex> getVertices(AddressSetView selection) {
|
||||||
|
if (selection.isEmpty()) {
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<AttributedVertex> vertices = new HashSet<>();
|
||||||
|
DataReferenceGraph graph = (DataReferenceGraph) graphDisplay.getGraph();
|
||||||
|
for (AddressRange range : selection) {
|
||||||
|
for (Address address : range) {
|
||||||
|
AttributedVertex vertex = graph.getVertex(graph.makeName(address));
|
||||||
|
if (vertex != null) {
|
||||||
|
vertices.add(vertex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return vertices;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected AddressSet getAddresses(Set<AttributedVertex> vertexIds) {
|
||||||
|
AddressSet addrSet = new AddressSet();
|
||||||
|
|
||||||
|
for (AttributedVertex vertex : vertexIds) {
|
||||||
|
Address addr = getAddress(vertex.getName());
|
||||||
|
if (addr != null) {
|
||||||
|
addrSet.add(addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return addrSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Address getAddress(String vertexIdString) {
|
||||||
|
AttributedVertex vertex = graphDisplay.getGraph().getVertex(vertexIdString);
|
||||||
|
return program.getAddressFactory()
|
||||||
|
.getAddress(vertex.getAttribute(DataReferenceGraph.ADDRESS_ATTRIBUTE));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getVertexId(Address address) {
|
||||||
|
DataReferenceGraph graph = (DataReferenceGraph) graphDisplay.getGraph();
|
||||||
|
return graph.makeName(address);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,158 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.graph.program;
|
||||||
|
|
||||||
|
import docking.widgets.EventTrigger;
|
||||||
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.address.AddressSet;
|
||||||
|
import ghidra.program.model.listing.CodeUnit;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
import ghidra.program.util.ProgramLocation;
|
||||||
|
import ghidra.program.util.ProgramSelection;
|
||||||
|
import ghidra.service.graph.*;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
import ghidra.util.exception.CancelledException;
|
||||||
|
import ghidra.util.exception.GraphException;
|
||||||
|
import ghidra.util.task.Task;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Task for creating and displaying a data reference graph
|
||||||
|
*/
|
||||||
|
public class DataReferenceGraphTask extends Task {
|
||||||
|
|
||||||
|
private String graphTitle;
|
||||||
|
private GraphDisplayProvider graphProvider;
|
||||||
|
private boolean reuseGraph;
|
||||||
|
private boolean appendGraph;
|
||||||
|
private PluginTool tool;
|
||||||
|
private Program program;
|
||||||
|
private int totalMaxDepth;
|
||||||
|
private int maxLabelLength;
|
||||||
|
private ProgramLocation location;
|
||||||
|
private DataReferenceGraph.Directions direction;
|
||||||
|
private AddressSet addresses;
|
||||||
|
private GraphDisplay display;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Constructor intended for creating a new graph
|
||||||
|
*/
|
||||||
|
public DataReferenceGraphTask(boolean reuseGraph, boolean appendToGraph, PluginTool tool,
|
||||||
|
ProgramSelection selection, ProgramLocation location,
|
||||||
|
GraphDisplayProvider graphProvider, int maxDepth, int maxLines,
|
||||||
|
DataReferenceGraph.Directions direction) {
|
||||||
|
super("Graph Data References", true, false, true);
|
||||||
|
|
||||||
|
this.reuseGraph = reuseGraph;
|
||||||
|
this.appendGraph = appendToGraph;
|
||||||
|
this.tool = tool;
|
||||||
|
this.graphProvider = graphProvider;
|
||||||
|
this.program = location.getProgram();
|
||||||
|
this.graphTitle = "Data references for: ";
|
||||||
|
this.totalMaxDepth = maxDepth;
|
||||||
|
this.maxLabelLength = maxLines;
|
||||||
|
this.location = location;
|
||||||
|
this.direction = direction;
|
||||||
|
this.display = null;
|
||||||
|
graphTitle = graphTitle + location.getAddress().toString();
|
||||||
|
|
||||||
|
Address locationAddress = location.getAddress();
|
||||||
|
addresses = new AddressSet(locationAddress);
|
||||||
|
if ((selection != null) && (selection.contains(locationAddress))) {
|
||||||
|
addresses.add(selection);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* grab current address and the code unit it is part of so we don't miss stuff assigned to say the structure */
|
||||||
|
Address unitAddress =
|
||||||
|
program.getListing().getCodeUnitContaining(locationAddress).getAddress();
|
||||||
|
addresses.add(unitAddress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* constructor intended for extending a graph in the same display
|
||||||
|
*/
|
||||||
|
public DataReferenceGraphTask(PluginTool tool, Program program, AddressSet addresses,
|
||||||
|
GraphDisplay display, int maxDepth, DataReferenceGraph.Directions direction) {
|
||||||
|
super("Graph Data References", true, false, true);
|
||||||
|
|
||||||
|
this.reuseGraph = true;
|
||||||
|
this.appendGraph = true;
|
||||||
|
this.tool = tool;
|
||||||
|
this.display = display;
|
||||||
|
this.program = program;
|
||||||
|
this.graphTitle = display.getGraphTitle();
|
||||||
|
this.totalMaxDepth = maxDepth;
|
||||||
|
this.maxLabelLength = 10;
|
||||||
|
this.direction = direction;
|
||||||
|
this.addresses = addresses;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(TaskMonitor monitor) throws CancelledException {
|
||||||
|
DataReferenceGraph graph = new DataReferenceGraph(program, totalMaxDepth);
|
||||||
|
|
||||||
|
monitor.setMessage("Generating Graph...");
|
||||||
|
monitor.setIndeterminate(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (CodeUnit unit : program.getListing().getCodeUnits(addresses, true)) {
|
||||||
|
monitor.checkCanceled();
|
||||||
|
AttributedVertex centerVertex =
|
||||||
|
graph.graphFrom(unit.getAddress(), direction, monitor);
|
||||||
|
/* TODO
|
||||||
|
* Want to make initial vertex easy to find, is this the best way?
|
||||||
|
*/
|
||||||
|
centerVertex.setAttribute("Color", "Orange");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (CancelledException e) {
|
||||||
|
monitor.setMessage("Cancelling");
|
||||||
|
graphTitle = graphTitle + " (partial)";
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (display == null) {
|
||||||
|
display = graphProvider.getGraphDisplay(reuseGraph, monitor);
|
||||||
|
display.defineEdgeAttribute(DataReferenceGraph.REF_SOURCE_ATTRIBUTE);
|
||||||
|
display.defineEdgeAttribute(DataReferenceGraph.REF_TYPE_ATTRIBUTE);
|
||||||
|
display.defineEdgeAttribute(DataReferenceGraph.REF_SYMBOL_ATTRIBUTE);
|
||||||
|
display.defineVertexAttribute(DataReferenceGraph.DATA_ATTRIBUTE);
|
||||||
|
display.setVertexLabelAttribute(DataReferenceGraph.LABEL_ATTRIBUTE,
|
||||||
|
GraphDisplay.ALIGN_LEFT, 12, true, maxLabelLength);
|
||||||
|
|
||||||
|
DataReferenceGraphDisplayListener listener =
|
||||||
|
new DataReferenceGraphDisplayListener(tool, display, program, totalMaxDepth);
|
||||||
|
display.setGraphDisplayListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
display.setGraph(graph, graphTitle, appendGraph, monitor);
|
||||||
|
|
||||||
|
if (location != null) {
|
||||||
|
// initialize the graph location, but don't have the graph send an event
|
||||||
|
AttributedVertex vertex = graph.getVertex(graph.makeName(location.getAddress()));
|
||||||
|
display.setFocusedVertex(vertex, EventTrigger.INTERNAL_ONLY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (GraphException e) {
|
||||||
|
if (!monitor.isCancelled()) {
|
||||||
|
Msg.showError(this, null, "Reference Graph Error",
|
||||||
|
"Unexpected error while graphing: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -73,6 +73,7 @@ public class ProgramGraphPlugin extends ProgramPlugin
|
||||||
private static final String REUSE_GRAPH = "Reuse Graph";
|
private static final String REUSE_GRAPH = "Reuse Graph";
|
||||||
private static final String GRAPH_ENTRY_POINT_NEXUS = "Graph Entry Point Nexus";
|
private static final String GRAPH_ENTRY_POINT_NEXUS = "Graph Entry Point Nexus";
|
||||||
private static final String FORCE_LOCATION_DISPLAY_OPTION = "Force Location Visible on Graph";
|
private static final String FORCE_LOCATION_DISPLAY_OPTION = "Force Location Visible on Graph";
|
||||||
|
private static final String MAX_DEPTH_OPTION = "Max Reference Depth";
|
||||||
public static final String MENU_GRAPH = "&Graph";
|
public static final String MENU_GRAPH = "&Graph";
|
||||||
|
|
||||||
private BlockModelService blockModelService;
|
private BlockModelService blockModelService;
|
||||||
|
@ -86,6 +87,7 @@ public class ProgramGraphPlugin extends ProgramPlugin
|
||||||
|
|
||||||
private boolean graphEntryPointNexus = false;
|
private boolean graphEntryPointNexus = false;
|
||||||
private int codeLimitPerBlock = 10;
|
private int codeLimitPerBlock = 10;
|
||||||
|
private int dataMaxDepth = 1;
|
||||||
|
|
||||||
private ToggleDockingAction forceLocationVisibleAction;
|
private ToggleDockingAction forceLocationVisibleAction;
|
||||||
|
|
||||||
|
@ -117,6 +119,8 @@ public class ProgramGraphPlugin extends ProgramPlugin
|
||||||
"Specifies whether or not " +
|
"Specifies whether or not " +
|
||||||
"graph displays should force the visible graph to pan and/or scale to ensure that focused " +
|
"graph displays should force the visible graph to pan and/or scale to ensure that focused " +
|
||||||
"locations are visible.");
|
"locations are visible.");
|
||||||
|
options.registerOption(MAX_DEPTH_OPTION, 1, help,
|
||||||
|
"Specifies max depth of data references to graph (0 for no limit)");
|
||||||
|
|
||||||
setOptions(options);
|
setOptions(options);
|
||||||
options.addOptionsChangeListener(this);
|
options.addOptionsChangeListener(this);
|
||||||
|
@ -170,21 +174,20 @@ public class ProgramGraphPlugin extends ProgramPlugin
|
||||||
if (reuseGraphAction != null) {
|
if (reuseGraphAction != null) {
|
||||||
reuseGraphAction.setSelected(reuseGraph);
|
reuseGraphAction.setSelected(reuseGraph);
|
||||||
}
|
}
|
||||||
|
dataMaxDepth = options.getInt(MAX_DEPTH_OPTION, dataMaxDepth);
|
||||||
// Note: we don't care about the FORCE_LOCATION_DISPLAY_OPTION. We register it, but its
|
// Note: we don't care about the FORCE_LOCATION_DISPLAY_OPTION. We register it, but its
|
||||||
// the actually the various GraphDisplays the make use of it.
|
// the actually the various GraphDisplays the make use of it.
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createActions() {
|
private void createActions() {
|
||||||
|
|
||||||
new ActionBuilder("Graph Block Flow", getName())
|
new ActionBuilder("Graph Block Flow", getName()).menuPath(MENU_GRAPH, "&Block Flow")
|
||||||
.menuPath(MENU_GRAPH, "&Block Flow")
|
|
||||||
.menuGroup("Graph", "A")
|
.menuGroup("Graph", "A")
|
||||||
.onAction(c -> graphBlockFlow())
|
.onAction(c -> graphBlockFlow())
|
||||||
.enabledWhen(this::canGraph)
|
.enabledWhen(this::canGraph)
|
||||||
.buildAndInstall(tool);
|
.buildAndInstall(tool);
|
||||||
|
|
||||||
new ActionBuilder("Graph Code Flow", getName())
|
new ActionBuilder("Graph Code Flow", getName()).menuPath(MENU_GRAPH, "C&ode Flow")
|
||||||
.menuPath(MENU_GRAPH, "C&ode Flow")
|
|
||||||
.menuGroup("Graph", "B")
|
.menuGroup("Graph", "B")
|
||||||
.onAction(c -> graphCodeFlow())
|
.onAction(c -> graphCodeFlow())
|
||||||
.enabledWhen(this::canGraph)
|
.enabledWhen(this::canGraph)
|
||||||
|
@ -197,22 +200,49 @@ public class ProgramGraphPlugin extends ProgramPlugin
|
||||||
.enabledWhen(this::canGraph)
|
.enabledWhen(this::canGraph)
|
||||||
.buildAndInstall(tool);
|
.buildAndInstall(tool);
|
||||||
|
|
||||||
reuseGraphAction = new ToggleActionBuilder("Reuse Graph", getName())
|
tool.setMenuGroup(new String[] { MENU_GRAPH, "Data" }, "Graph", "Data");
|
||||||
.menuPath(MENU_GRAPH, "Reuse Graph")
|
HelpLocation helpLoc = new HelpLocation(getName(), "Data_Reference_Graph");
|
||||||
.menuGroup("Graph Options")
|
|
||||||
.selected(reuseGraph)
|
new ActionBuilder("Graph To/From Data References", getName())
|
||||||
.onAction(c -> reuseGraph = reuseGraphAction.isSelected())
|
.menuPath(MENU_GRAPH, "Data", "To/From &References")
|
||||||
|
.menuGroup(MENU_GRAPH, "Data")
|
||||||
|
.helpLocation(helpLoc)
|
||||||
|
.onAction(c -> graphDataReferences())
|
||||||
.enabledWhen(this::canGraph)
|
.enabledWhen(this::canGraph)
|
||||||
.buildAndInstall(tool);
|
.buildAndInstall(tool);
|
||||||
|
|
||||||
appendGraphAction = new ToggleActionBuilder("Append Graph", getName())
|
new ActionBuilder("Graph To Data References", getName())
|
||||||
.menuPath(MENU_GRAPH, "Append Graph")
|
.menuPath(MENU_GRAPH, "Data", "&To References")
|
||||||
.menuGroup("Graph Options")
|
.menuGroup(MENU_GRAPH, "Data")
|
||||||
.selected(false)
|
.helpLocation(helpLoc)
|
||||||
.onAction(c -> updateAppendAndReuseGraph())
|
.onAction(c -> graphToDataReferences())
|
||||||
.enabledWhen(this::canGraph)
|
.enabledWhen(this::canGraph)
|
||||||
.buildAndInstall(tool);
|
.buildAndInstall(tool);
|
||||||
|
|
||||||
|
new ActionBuilder("Graph From Data References", getName())
|
||||||
|
.menuPath(MENU_GRAPH, "Data", "&From References")
|
||||||
|
.menuGroup(MENU_GRAPH, "Data")
|
||||||
|
.helpLocation(helpLoc)
|
||||||
|
.onAction(c -> graphFromDataReferences())
|
||||||
|
.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);
|
||||||
|
|
||||||
|
appendGraphAction =
|
||||||
|
new ToggleActionBuilder("Append Graph", getName()).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())
|
forceLocationVisibleAction = new ToggleActionBuilder("Show Location in Graph", getName())
|
||||||
.menuPath(MENU_GRAPH, "Show Location")
|
.menuPath(MENU_GRAPH, "Show Location")
|
||||||
.description("Tell the graph to pan/scale as need to keep location changes visible")
|
.description("Tell the graph to pan/scale as need to keep location changes visible")
|
||||||
|
@ -263,13 +293,13 @@ public class ProgramGraphPlugin extends ProgramPlugin
|
||||||
subUsingGraphActions.add(action);
|
subUsingGraphActions.add(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
tool.setMenuGroup(new String[] { "Graph", "Calls Using Model" }, "Graph");
|
tool.setMenuGroup(new String[] { MENU_GRAPH, "Calls Using Model" }, "Graph", "C");
|
||||||
}
|
}
|
||||||
|
|
||||||
private DockingAction buildGraphActionWithModel(String blockModelName, HelpLocation helpLoc) {
|
private DockingAction buildGraphActionWithModel(String blockModelName, HelpLocation helpLoc) {
|
||||||
return new ActionBuilder("Graph Calls using " + blockModelName, getName())
|
return new ActionBuilder("Graph Calls using " + blockModelName, getName())
|
||||||
.menuPath("Graph", "Calls Using Model", blockModelName)
|
.menuPath(MENU_GRAPH, "Calls Using Model", blockModelName)
|
||||||
.menuGroup("Graph")
|
.menuGroup(MENU_GRAPH, "C")
|
||||||
.helpLocation(helpLoc)
|
.helpLocation(helpLoc)
|
||||||
.onAction(c -> graphSubroutinesUsing(blockModelName))
|
.onAction(c -> graphSubroutinesUsing(blockModelName))
|
||||||
.enabledWhen(this::canGraph)
|
.enabledWhen(this::canGraph)
|
||||||
|
@ -292,14 +322,25 @@ public class ProgramGraphPlugin extends ProgramPlugin
|
||||||
graph("Call Graph (" + modelName + ")", modelName, false);
|
graph("Call Graph (" + modelName + ")", modelName, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void graphDataReferences() {
|
||||||
|
graphData(DataReferenceGraph.Directions.BOTH_WAYS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void graphToDataReferences() {
|
||||||
|
graphData(DataReferenceGraph.Directions.TO_ONLY);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void graphFromDataReferences() {
|
||||||
|
graphData(DataReferenceGraph.Directions.FROM_ONLY);
|
||||||
|
}
|
||||||
|
|
||||||
private void graph(String actionName, String modelName, boolean showCode) {
|
private void graph(String actionName, String modelName, boolean showCode) {
|
||||||
try {
|
try {
|
||||||
CodeBlockModel model =
|
CodeBlockModel model =
|
||||||
blockModelService.getNewModelByName(modelName, currentProgram, true);
|
blockModelService.getNewModelByName(modelName, currentProgram, true);
|
||||||
BlockGraphTask task =
|
BlockGraphTask task = new BlockGraphTask(actionName, graphEntryPointNexus, showCode,
|
||||||
new BlockGraphTask(actionName, graphEntryPointNexus, showCode, reuseGraph,
|
reuseGraph, appendToGraph, tool, currentSelection, currentLocation, model,
|
||||||
appendToGraph, tool, currentSelection, currentLocation, model,
|
defaultGraphService);
|
||||||
defaultGraphService);
|
|
||||||
task.setCodeLimitPerBlock(codeLimitPerBlock);
|
task.setCodeLimitPerBlock(codeLimitPerBlock);
|
||||||
new TaskLauncher(task, tool.getToolFrame());
|
new TaskLauncher(task, tool.getToolFrame());
|
||||||
}
|
}
|
||||||
|
@ -309,6 +350,13 @@ public class ProgramGraphPlugin extends ProgramPlugin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void graphData(DataReferenceGraph.Directions direction) {
|
||||||
|
DataReferenceGraphTask task =
|
||||||
|
new DataReferenceGraphTask(reuseGraph, appendToGraph, tool, currentSelection,
|
||||||
|
currentLocation, defaultGraphService, dataMaxDepth, codeLimitPerBlock, direction);
|
||||||
|
new TaskLauncher(task, tool.getToolFrame());
|
||||||
|
}
|
||||||
|
|
||||||
String getProgramName() {
|
String getProgramName() {
|
||||||
return currentProgram != null ? currentProgram.getName() : null;
|
return currentProgram != null ? currentProgram.getName() : null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,128 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.graph.program;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
|
||||||
|
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||||
|
import ghidra.app.services.ProgramManager;
|
||||||
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
|
import ghidra.framework.plugintool.util.PluginException;
|
||||||
|
import ghidra.program.database.ProgramDB;
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.address.AddressSet;
|
||||||
|
import ghidra.program.model.data.*;
|
||||||
|
import ghidra.program.model.mem.MemoryAccessException;
|
||||||
|
import ghidra.program.model.symbol.RefType;
|
||||||
|
import ghidra.program.model.symbol.SourceType;
|
||||||
|
import ghidra.test.*;
|
||||||
|
|
||||||
|
public class AbstractDataReferenceGraphTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
|
protected PluginTool tool;
|
||||||
|
protected ProgramDB program;
|
||||||
|
protected TestEnv env;
|
||||||
|
private ToyProgramBuilder builder;
|
||||||
|
protected CodeBrowserPlugin codeBrowser;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
|
||||||
|
setErrorGUIEnabled(false);
|
||||||
|
|
||||||
|
env = new TestEnv();
|
||||||
|
tool = env.getTool();
|
||||||
|
|
||||||
|
initializeTool();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
env.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void initializeTool() throws Exception {
|
||||||
|
installPlugins();
|
||||||
|
|
||||||
|
openProgram();
|
||||||
|
ProgramManager pm = tool.getService(ProgramManager.class);
|
||||||
|
pm.openProgram(program.getDomainFile());
|
||||||
|
|
||||||
|
showTool(tool);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void installPlugins() throws PluginException {
|
||||||
|
tool.addPlugin(CodeBrowserPlugin.class.getName());
|
||||||
|
codeBrowser = env.getPlugin(CodeBrowserPlugin.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void openProgram() throws Exception {
|
||||||
|
|
||||||
|
builder = new ToyProgramBuilder("sample", true);
|
||||||
|
builder.createMemory("data", "0x01000000", 64);
|
||||||
|
builder.createMemory("caller", "0x01002200", 8);
|
||||||
|
|
||||||
|
buildFunction();
|
||||||
|
buildData();
|
||||||
|
|
||||||
|
program = builder.getProgram();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void buildData() throws Exception {
|
||||||
|
builder.createString("0x01000000", "thing here", StandardCharsets.US_ASCII, true,
|
||||||
|
StringDataType.dataType);
|
||||||
|
builder.createMemoryReference("0x0100000c", "0x0100000f", RefType.DATA,
|
||||||
|
SourceType.ANALYSIS);
|
||||||
|
builder.createString("0x0100000f", "another thing", StandardCharsets.US_ASCII, true,
|
||||||
|
StringDataType.dataType);
|
||||||
|
builder.addDataType(IntegerDataType.dataType);
|
||||||
|
builder.createMemoryReference("0x01000021", "0x0100000c", RefType.DATA,
|
||||||
|
SourceType.ANALYSIS);
|
||||||
|
|
||||||
|
Structure pointerStructure = new StructureDataType("pointer_thing", 0);
|
||||||
|
pointerStructure.setPackingEnabled(true);
|
||||||
|
pointerStructure.add(IntegerDataType.dataType, "num", null);
|
||||||
|
pointerStructure.add(PointerDataType.dataType, "ptr", null);
|
||||||
|
builder.addDataType(pointerStructure);
|
||||||
|
builder.applyDataType("0x0100001d", pointerStructure);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void buildFunction() throws MemoryAccessException {
|
||||||
|
// just a function that calls another
|
||||||
|
builder.createMemoryReference("0x1002200", "0x01000000", RefType.DATA, SourceType.ANALYSIS);
|
||||||
|
builder.addBytesCall("0x01002201", "0x01002239");// jump to C
|
||||||
|
builder.addBytesReturn("0x01002203");
|
||||||
|
|
||||||
|
builder.disassemble("0x01002200", 4, true);
|
||||||
|
builder.createFunction("0x01002200");
|
||||||
|
builder.createLabel("0x01002200", "entry");// function label
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Address addr(long addr) {
|
||||||
|
return builder.getAddress(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Address addr(String addressString) {
|
||||||
|
return builder.addr(addressString);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected AddressSet addrSet(long start, long end) {
|
||||||
|
return new AddressSet(addr(start), addr(end));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,111 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.graph.program;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ghidra.program.model.address.AddressSet;
|
||||||
|
import ghidra.program.util.ProgramLocation;
|
||||||
|
import ghidra.program.util.ProgramSelection;
|
||||||
|
import ghidra.service.graph.AttributedVertex;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
|
public class DataReferenceGraphEventTest extends AbstractDataReferenceGraphTest {
|
||||||
|
|
||||||
|
private TestGraphDisplay display;
|
||||||
|
private DataReferenceGraph graph;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
TestGraphService graphService = new TestGraphService();
|
||||||
|
ProgramLocation location = new ProgramLocation(program, addr(0x01000000));
|
||||||
|
DataReferenceGraphTask task = new DataReferenceGraphTask(false, false, tool, null, location,
|
||||||
|
graphService, 0, 10, DataReferenceGraph.Directions.BOTH_WAYS);
|
||||||
|
|
||||||
|
task.monitoredRun(TaskMonitor.DUMMY);
|
||||||
|
|
||||||
|
display = (TestGraphDisplay) graphService.getGraphDisplay(true, TaskMonitor.DUMMY);
|
||||||
|
DataReferenceGraph otherGraph = new DataReferenceGraph(program, 0);
|
||||||
|
otherGraph.graphFrom(addr(0x0100001d), DataReferenceGraph.Directions.BOTH_WAYS,
|
||||||
|
TaskMonitor.DUMMY);
|
||||||
|
display.setGraph(otherGraph, "testing", true, TaskMonitor.DUMMY);
|
||||||
|
graph = (DataReferenceGraph) display.getGraph();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGhidraLocationChanged() {
|
||||||
|
codeBrowser.goTo(new ProgramLocation(program, addr(0x01002200)));
|
||||||
|
assertEquals("01002200",
|
||||||
|
display.getFocusedVertex().getAttribute(DataReferenceGraph.ADDRESS_ATTRIBUTE));
|
||||||
|
|
||||||
|
codeBrowser.goTo(new ProgramLocation(program, addr(0x1000000)));
|
||||||
|
assertEquals("01000000",
|
||||||
|
display.getFocusedVertex().getAttribute(DataReferenceGraph.ADDRESS_ATTRIBUTE));
|
||||||
|
|
||||||
|
// also try a location that is not the start of a block
|
||||||
|
codeBrowser.goTo(new ProgramLocation(program, addr(0x1000021)));
|
||||||
|
assertEquals("0100001d",
|
||||||
|
display.getFocusedVertex().getAttribute(DataReferenceGraph.ADDRESS_ATTRIBUTE));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGhidraSelectionChanged() {
|
||||||
|
makeSelection(tool, program, addrSet(0x1000000, 0x100001c));
|
||||||
|
Set<AttributedVertex> selected = new HashSet<>(display.getSelectedVertices());
|
||||||
|
assertEquals(3, selected.size());
|
||||||
|
assertTrue(selected.contains(graph.getVertex(graph.makeName(addr("01000000")))));
|
||||||
|
assertTrue(selected.contains(graph.getVertex(graph.makeName(addr("0100000c")))));
|
||||||
|
assertTrue(selected.contains(graph.getVertex(graph.makeName(addr("0100000f")))));
|
||||||
|
|
||||||
|
makeSelection(tool, program, new AddressSet(addr(0x100000f), addr(0x1000021)));
|
||||||
|
selected = new HashSet<>(display.getSelectedVertices());
|
||||||
|
assertEquals(2, selected.size());
|
||||||
|
assertTrue(selected.contains(graph.getVertex(graph.makeName(addr("0100000f")))));
|
||||||
|
assertTrue(selected.contains(graph.getVertex(graph.makeName(addr("01000021")))));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGraphNodeFocused() {
|
||||||
|
display.focusChanged(graph.getVertex(graph.makeName(addr(0x01002200))));
|
||||||
|
assertEquals(addr(0x01002200), codeBrowser.getCurrentLocation().getAddress());
|
||||||
|
|
||||||
|
display.focusChanged(graph.getVertex(graph.makeName(addr("01000000"))));
|
||||||
|
assertEquals(addr(0x01000000), codeBrowser.getCurrentLocation().getAddress());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGraphNodesSelected() {
|
||||||
|
display.selectionChanged(Set.of(graph.getVertex(graph.makeName(addr("01000000"))),
|
||||||
|
graph.getVertex(graph.makeName(addr("0100000c")))));
|
||||||
|
ProgramSelection selection = codeBrowser.getCurrentSelection();
|
||||||
|
assertEquals(addr(0x01000000), selection.getMinAddress());
|
||||||
|
assertEquals(addr(0x0100000c), selection.getMaxAddress());
|
||||||
|
|
||||||
|
display.selectionChanged(Set.of(graph.getVertex(graph.makeName(addr("0100000f"))),
|
||||||
|
graph.getVertex(graph.makeName(addr("01000021")))));
|
||||||
|
selection = codeBrowser.getCurrentSelection();
|
||||||
|
assertEquals(addr(0x0100000f), selection.getMinAddress());
|
||||||
|
assertEquals(addr(0x01000024), selection.getMaxAddress());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,264 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.graph.program;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ghidra.program.util.ProgramLocation;
|
||||||
|
import ghidra.program.util.ProgramSelection;
|
||||||
|
import ghidra.service.graph.AttributedEdge;
|
||||||
|
import ghidra.service.graph.AttributedVertex;
|
||||||
|
import ghidra.util.exception.GraphException;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
|
public class DataReferenceGraphTaskTest extends AbstractDataReferenceGraphTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGraphWithLimit() throws GraphException {
|
||||||
|
|
||||||
|
TestGraphService graphService = new TestGraphService();
|
||||||
|
DataReferenceGraphTask task = new DataReferenceGraphTask(false, false, tool, null,
|
||||||
|
new ProgramLocation(program, addr(0x0100001d)), graphService, 1, 10,
|
||||||
|
DataReferenceGraph.Directions.BOTH_WAYS);
|
||||||
|
task.monitoredRun(TaskMonitor.DUMMY);
|
||||||
|
|
||||||
|
TestGraphDisplay display =
|
||||||
|
(TestGraphDisplay) graphService.getGraphDisplay(true, TaskMonitor.DUMMY);
|
||||||
|
DataReferenceGraph graph = (DataReferenceGraph) display.getGraph();
|
||||||
|
|
||||||
|
assertEquals(2, graph.getVertexCount());
|
||||||
|
AttributedVertex v1 = graph.getVertex(graph.makeName(addr(0x0100001d)));
|
||||||
|
AttributedVertex v2 = graph.getVertex(graph.makeName(addr(0x0100000c)));
|
||||||
|
AttributedEdge e1 = graph.getEdge(v1, v2);
|
||||||
|
assertNotNull(v1);
|
||||||
|
assertNotNull(v2);
|
||||||
|
assertNotNull(e1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGraphWithoutLimit() throws GraphException {
|
||||||
|
|
||||||
|
TestGraphService graphService = new TestGraphService();
|
||||||
|
DataReferenceGraphTask task = new DataReferenceGraphTask(false, false, tool, null,
|
||||||
|
new ProgramLocation(program, addr(0x0100001d)), graphService, 0, 10,
|
||||||
|
DataReferenceGraph.Directions.BOTH_WAYS);
|
||||||
|
task.monitoredRun(TaskMonitor.DUMMY);
|
||||||
|
|
||||||
|
TestGraphDisplay display =
|
||||||
|
(TestGraphDisplay) graphService.getGraphDisplay(true, TaskMonitor.DUMMY);
|
||||||
|
DataReferenceGraph graph = (DataReferenceGraph) display.getGraph();
|
||||||
|
|
||||||
|
assertEquals(3, graph.getVertexCount());
|
||||||
|
AttributedVertex v1 = graph.getVertex(graph.makeName(addr(0x0100001d)));
|
||||||
|
AttributedVertex v2 = graph.getVertex(graph.makeName(addr(0x0100000c)));
|
||||||
|
AttributedVertex v3 = graph.getVertex(graph.makeName(addr(0x0100000f)));
|
||||||
|
AttributedEdge e1 = graph.getEdge(v1, v2);
|
||||||
|
AttributedEdge e2 = graph.getEdge(v2, v3);
|
||||||
|
assertNotNull(v1);
|
||||||
|
assertNotNull(v2);
|
||||||
|
assertNotNull(v3);
|
||||||
|
assertNotNull(e1);
|
||||||
|
assertNotNull(e2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGraphAdd() throws GraphException {
|
||||||
|
TestGraphService graphService = new TestGraphService();
|
||||||
|
DataReferenceGraphTask task = new DataReferenceGraphTask(false, false, tool, null,
|
||||||
|
new ProgramLocation(program, addr(0x0100001d)), graphService, 1, 10,
|
||||||
|
DataReferenceGraph.Directions.BOTH_WAYS);
|
||||||
|
task.monitoredRun(TaskMonitor.DUMMY);
|
||||||
|
|
||||||
|
TestGraphDisplay display =
|
||||||
|
(TestGraphDisplay) graphService.getGraphDisplay(true, TaskMonitor.DUMMY);
|
||||||
|
DataReferenceGraph graph = (DataReferenceGraph) display.getGraph();
|
||||||
|
|
||||||
|
assertEquals(2, graph.getVertexCount());
|
||||||
|
AttributedVertex v1 = graph.getVertex(graph.makeName(addr(0x0100001d)));
|
||||||
|
AttributedVertex v2 = graph.getVertex(graph.makeName(addr(0x0100000c)));
|
||||||
|
AttributedEdge e1 = graph.getEdge(v1, v2);
|
||||||
|
assertNotNull(v1);
|
||||||
|
assertNotNull(v2);
|
||||||
|
assertNotNull(e1);
|
||||||
|
|
||||||
|
DataReferenceGraphTask taskAdd = new DataReferenceGraphTask(tool, program,
|
||||||
|
addrSet(0x0100000c, 0x0100000c), display, 1, DataReferenceGraph.Directions.BOTH_WAYS);
|
||||||
|
taskAdd.monitoredRun(TaskMonitor.DUMMY);
|
||||||
|
graph = (DataReferenceGraph) display.getGraph();
|
||||||
|
assertEquals(3, graph.getVertexCount());
|
||||||
|
AttributedVertex v1Add = graph.getVertex(graph.makeName(addr(0x0100001d)));
|
||||||
|
AttributedVertex v2Add = graph.getVertex(graph.makeName(addr(0x0100000c)));
|
||||||
|
AttributedVertex v3Add = graph.getVertex(graph.makeName(addr(0x0100000f)));
|
||||||
|
AttributedEdge e1Add = graph.getEdge(v1Add, v2Add);
|
||||||
|
AttributedEdge e2Add = graph.getEdge(v2Add, v3Add);
|
||||||
|
assertNotNull(v1Add);
|
||||||
|
assertNotNull(v2Add);
|
||||||
|
assertNotNull(v3Add);
|
||||||
|
assertNotNull(e1Add);
|
||||||
|
assertNotNull(e2Add);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDirectionsBoth() throws GraphException {
|
||||||
|
TestGraphService graphService = new TestGraphService();
|
||||||
|
DataReferenceGraphTask task = new DataReferenceGraphTask(false, false, tool, null,
|
||||||
|
new ProgramLocation(program, addr(0x0100000c)), graphService, 0, 10,
|
||||||
|
DataReferenceGraph.Directions.BOTH_WAYS);
|
||||||
|
task.monitoredRun(TaskMonitor.DUMMY);
|
||||||
|
|
||||||
|
TestGraphDisplay display =
|
||||||
|
(TestGraphDisplay) graphService.getGraphDisplay(true, TaskMonitor.DUMMY);
|
||||||
|
DataReferenceGraph graph = (DataReferenceGraph) display.getGraph();
|
||||||
|
|
||||||
|
assertEquals(3, graph.getVertexCount());
|
||||||
|
AttributedVertex v1 = graph.getVertex(graph.makeName(addr(0x0100001d)));
|
||||||
|
AttributedVertex v2 = graph.getVertex(graph.makeName(addr(0x0100000c)));
|
||||||
|
AttributedVertex v3 = graph.getVertex(graph.makeName(addr(0x0100000f)));
|
||||||
|
AttributedEdge e1 = graph.getEdge(v1, v2);
|
||||||
|
AttributedEdge e2 = graph.getEdge(v2, v3);
|
||||||
|
assertNotNull(v1);
|
||||||
|
assertNotNull(v2);
|
||||||
|
assertNotNull(v3);
|
||||||
|
assertNotNull(e1);
|
||||||
|
assertNotNull(e2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDirectionsTo() throws GraphException {
|
||||||
|
TestGraphService graphService = new TestGraphService();
|
||||||
|
DataReferenceGraphTask task = new DataReferenceGraphTask(false, false, tool, null,
|
||||||
|
new ProgramLocation(program, addr(0x0100000c)), graphService, 0, 10,
|
||||||
|
DataReferenceGraph.Directions.TO_ONLY);
|
||||||
|
task.monitoredRun(TaskMonitor.DUMMY);
|
||||||
|
|
||||||
|
TestGraphDisplay display =
|
||||||
|
(TestGraphDisplay) graphService.getGraphDisplay(true, TaskMonitor.DUMMY);
|
||||||
|
DataReferenceGraph graph = (DataReferenceGraph) display.getGraph();
|
||||||
|
|
||||||
|
assertEquals(2, graph.getVertexCount());
|
||||||
|
AttributedVertex v1 = graph.getVertex(graph.makeName(addr(0x0100001d)));
|
||||||
|
AttributedVertex v2 = graph.getVertex(graph.makeName(addr(0x0100000c)));
|
||||||
|
AttributedVertex v3 = graph.getVertex(graph.makeName(addr(0x0100000f)));
|
||||||
|
AttributedEdge e1 = graph.getEdge(v1, v2);
|
||||||
|
AttributedEdge e2 = graph.getEdge(v2, v3);
|
||||||
|
assertNotNull(v1);
|
||||||
|
assertNotNull(v2);
|
||||||
|
assertNull(v3);
|
||||||
|
assertNotNull(e1);
|
||||||
|
assertNull(e2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDirectionsFrom() throws GraphException {
|
||||||
|
TestGraphService graphService = new TestGraphService();
|
||||||
|
DataReferenceGraphTask task = new DataReferenceGraphTask(false, false, tool, null,
|
||||||
|
new ProgramLocation(program, addr(0x0100000c)), graphService, 0, 10,
|
||||||
|
DataReferenceGraph.Directions.FROM_ONLY);
|
||||||
|
task.monitoredRun(TaskMonitor.DUMMY);
|
||||||
|
|
||||||
|
TestGraphDisplay display =
|
||||||
|
(TestGraphDisplay) graphService.getGraphDisplay(true, TaskMonitor.DUMMY);
|
||||||
|
DataReferenceGraph graph = (DataReferenceGraph) display.getGraph();
|
||||||
|
|
||||||
|
assertEquals(2, graph.getVertexCount());
|
||||||
|
AttributedVertex v1 = graph.getVertex(graph.makeName(addr(0x0100001d)));
|
||||||
|
AttributedVertex v2 = graph.getVertex(graph.makeName(addr(0x0100000c)));
|
||||||
|
AttributedVertex v3 = graph.getVertex(graph.makeName(addr(0x0100000f)));
|
||||||
|
AttributedEdge e1 = graph.getEdge(v1, v2);
|
||||||
|
AttributedEdge e2 = graph.getEdge(v2, v3);
|
||||||
|
assertNull(v1);
|
||||||
|
assertNotNull(v2);
|
||||||
|
assertNotNull(v3);
|
||||||
|
assertNull(e1);
|
||||||
|
assertNotNull(e2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNodeWithType() throws GraphException {
|
||||||
|
TestGraphService graphService = new TestGraphService();
|
||||||
|
DataReferenceGraphTask task = new DataReferenceGraphTask(false, false, tool, null,
|
||||||
|
new ProgramLocation(program, addr(0x0100001d)), graphService, 1, 10,
|
||||||
|
DataReferenceGraph.Directions.BOTH_WAYS);
|
||||||
|
task.monitoredRun(TaskMonitor.DUMMY);
|
||||||
|
|
||||||
|
TestGraphDisplay display =
|
||||||
|
(TestGraphDisplay) graphService.getGraphDisplay(true, TaskMonitor.DUMMY);
|
||||||
|
DataReferenceGraph graph = (DataReferenceGraph) display.getGraph();
|
||||||
|
|
||||||
|
AttributedVertex vertex = graph.getVertex(graph.makeName(addr(0x0100001d)));
|
||||||
|
assertEquals("pointer_thing", vertex.getAttribute(DataReferenceGraph.DATA_ATTRIBUTE));
|
||||||
|
assertEquals("0100001d\npointer_thing",
|
||||||
|
vertex.getAttribute(DataReferenceGraph.LABEL_ATTRIBUTE));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCodeReference() throws GraphException {
|
||||||
|
TestGraphService graphService = new TestGraphService();
|
||||||
|
DataReferenceGraphTask task = new DataReferenceGraphTask(false, false, tool, null,
|
||||||
|
new ProgramLocation(program, addr(0x01002200)), graphService, 0, 10,
|
||||||
|
DataReferenceGraph.Directions.BOTH_WAYS);
|
||||||
|
task.monitoredRun(TaskMonitor.DUMMY);
|
||||||
|
|
||||||
|
TestGraphDisplay display =
|
||||||
|
(TestGraphDisplay) graphService.getGraphDisplay(true, TaskMonitor.DUMMY);
|
||||||
|
DataReferenceGraph graph = (DataReferenceGraph) display.getGraph();
|
||||||
|
|
||||||
|
assertEquals(2, graph.getVertexCount());
|
||||||
|
AttributedVertex v1 = graph.getVertex(graph.makeName(addr(0x01002200)));
|
||||||
|
AttributedVertex v2 = graph.getVertex(graph.makeName(addr(0x01000000)));
|
||||||
|
AttributedEdge e1 = graph.getEdge(v1, v2);
|
||||||
|
assertNotNull(v1);
|
||||||
|
assertNotNull(v2);
|
||||||
|
assertNotNull(e1);
|
||||||
|
assertEquals("TriangleDown", v1.getAttribute("Icon"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGraphSelection() throws GraphException {
|
||||||
|
ProgramSelection selection = new ProgramSelection(addr(0x01000000), addr(0x0100000c));
|
||||||
|
TestGraphService graphService = new TestGraphService();
|
||||||
|
DataReferenceGraphTask task = new DataReferenceGraphTask(false, false, tool, selection,
|
||||||
|
new ProgramLocation(program, addr(0x0100000c)), graphService, 0, 10,
|
||||||
|
DataReferenceGraph.Directions.BOTH_WAYS);
|
||||||
|
task.monitoredRun(TaskMonitor.DUMMY);
|
||||||
|
|
||||||
|
TestGraphDisplay display =
|
||||||
|
(TestGraphDisplay) graphService.getGraphDisplay(true, TaskMonitor.DUMMY);
|
||||||
|
DataReferenceGraph graph = (DataReferenceGraph) display.getGraph();
|
||||||
|
|
||||||
|
//there's an extra vertex at 0x0100000b as an artifact of selection construction
|
||||||
|
assertEquals(6, graph.getVertexCount());
|
||||||
|
AttributedVertex v1 = graph.getVertex(graph.makeName(addr(0x0100001d)));
|
||||||
|
AttributedVertex v2 = graph.getVertex(graph.makeName(addr(0x0100000c)));
|
||||||
|
AttributedVertex v3 = graph.getVertex(graph.makeName(addr(0x0100000f)));
|
||||||
|
AttributedVertex v4 = graph.getVertex(graph.makeName(addr(0x01002200)));
|
||||||
|
AttributedVertex v5 = graph.getVertex(graph.makeName(addr(0x01000000)));
|
||||||
|
AttributedEdge e3 = graph.getEdge(v4, v5);
|
||||||
|
AttributedEdge e1 = graph.getEdge(v1, v2);
|
||||||
|
AttributedEdge e2 = graph.getEdge(v2, v3);
|
||||||
|
assertNotNull(v1);
|
||||||
|
assertNotNull(v2);
|
||||||
|
assertNotNull(v3);
|
||||||
|
assertNotNull(v4);
|
||||||
|
assertNotNull(v5);
|
||||||
|
assertNotNull(e1);
|
||||||
|
assertNotNull(e2);
|
||||||
|
assertNotNull(e3);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue