GP-808 Added Display as Graph action to the Datatype Manager

This commit is contained in:
ghidra42 2021-02-26 19:10:08 +00:00
parent 22675e9f39
commit ae40102420
7 changed files with 523 additions and 3 deletions

View file

@ -725,8 +725,15 @@
<P>For Typedefs, Pointers and Arrays, this action will navigate to the tree node of the
type to which respective refers.</P>
</BLOCKQUOTE>
<H3><A name="Type_Graph">Display Data Type as Graph</A></H3>
<BLOCKQUOTE>
<P>For Structures, Unions and Pointers, will generate a graph of the type, with nodes
for each enclosed structure or pointed to type</P>
</BLOCKQUOTE>
</BLOCKQUOTE>
<P class="providedbyplugin">Provided by: <I>DataTypeManagerPlugin</I></P>

View file

@ -191,8 +191,9 @@ public class DataTypesProvider extends ComponentProviderAdapter {
addLocalAction(new FindReferencesToFieldAction(plugin)); // DataType
// addLocalAction( new FindDataTypesContainingAction(plugin) ); // DataType
addLocalAction(new FindBaseDataTypeAction(plugin)); // DataType
addLocalAction(new DisplayTypeAsGraphAction(plugin));
// toolbar actions
// toolbar actions
previousAction = new NextPreviousDataTypeAction(this, plugin.getName(), false);
addLocalAction(previousAction);
nextAction = new NextPreviousDataTypeAction(this, plugin.getName(), true);
@ -344,8 +345,8 @@ public class DataTypesProvider extends ComponentProviderAdapter {
isToolbarAction = false;
}
return new DataTypesActionContext(this, plugin.getProgram(), archiveGTree,
clickedNode, isToolbarAction);
return new DataTypesActionContext(this, plugin.getProgram(), archiveGTree, clickedNode,
isToolbarAction);
}
@Override // overridden to handle special logic in plugin

View file

@ -0,0 +1,120 @@
/* ###
* 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.app.plugin.core.datamgr.actions;
import javax.swing.tree.TreePath;
import docking.ActionContext;
import docking.action.DockingAction;
import docking.action.MenuData;
import docking.widgets.tree.GTree;
import docking.widgets.tree.GTreeNode;
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
import ghidra.app.plugin.core.datamgr.DataTypesActionContext;
import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
import ghidra.app.services.GraphDisplayBroker;
import ghidra.program.model.data.*;
import ghidra.service.graph.GraphDisplayProvider;
import ghidra.util.HelpLocation;
import ghidra.util.Msg;
import ghidra.util.task.TaskLauncher;
/*
* Action to display a Composite data type as a graph from the Data Type Manager
*
* The graphing is done recursively in a separate task
*/
public class DisplayTypeAsGraphAction extends DockingAction {
private DataTypeManagerPlugin plugin;
/*
* Constructor
*
* @param plugin the plugin this action is contained in
*/
public DisplayTypeAsGraphAction(DataTypeManagerPlugin plugin) {
super("Display Data Type as Graph", plugin.getName());
this.plugin = plugin;
String menuGroup = "ZVeryLast"; // it's own group; on the bottom
setPopupMenuData(new MenuData(new String[] { "Display as Graph" }, null, menuGroup));
setHelpLocation(new HelpLocation("DataTypeManagerPlugin", "Type_Graph"));
}
@Override
public void actionPerformed(ActionContext context) {
GraphDisplayBroker broker = plugin.getTool().getService(GraphDisplayBroker.class);
if (broker == null) {
Msg.showError(this, null, "Missing Plugin", "The Graph plugin is not installed.\n" +
"Please add the plugin implementing this service.");
return;
}
GraphDisplayProvider service = broker.getDefaultGraphDisplayProvider();
GTree gTree = (GTree) context.getContextObject();
TreePath[] selectionPaths = gTree.getSelectionPaths();
for (TreePath path : selectionPaths) {
GTreeNode node = (GTreeNode) path.getLastPathComponent();
if (!(node instanceof DataTypeNode)) {
continue;
}
DataTypeNode dataTypeNode = (DataTypeNode) node;
DataType dt = dataTypeNode.getDataType();
if (dt instanceof TypeDef) {
dt = ((TypeDef) dt).getBaseDataType();
}
if (dt instanceof Composite || dt instanceof Pointer) {
TypeGraphTask task = new TypeGraphTask(dataTypeNode.getDataType(), service);
new TaskLauncher(task, plugin.getTool().getToolFrame());
}
}
}
@Override
public boolean isEnabledForContext(ActionContext context) {
boolean enabled = false;
if (!(context instanceof DataTypesActionContext)) {
return enabled;
}
Object contextObject = context.getContextObject();
GTree gtree = (GTree) contextObject;
TreePath[] selectionPaths = gtree.getSelectionPaths();
for (TreePath path : selectionPaths) {
GTreeNode node = (GTreeNode) path.getLastPathComponent();
if (!(node instanceof DataTypeNode)) {
continue;
}
DataTypeNode dtNode = (DataTypeNode) node;
DataType dt = dtNode.getDataType();
if (dt instanceof TypeDef) {
dt = ((TypeDef) dt).getBaseDataType();
}
if (dt instanceof Composite || dt instanceof Pointer) {
enabled = true;
}
}
return enabled;
}
}

View file

@ -0,0 +1,141 @@
/* ###
* 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.app.plugin.core.datamgr.actions;
import ghidra.program.model.data.*;
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 to recursively graph and display a data type
*
* Nodes are generated for pointers and embedded structures
*/
public class TypeGraphTask extends Task {
private DataType type;
private String graphTitle;
private GraphDisplayProvider graphService;
public static final String TYPE_ATTRIBUTE = "Type";
public static final String EMBEDDED = "Composite";
public static final String POINTER = "Reference";
public static final String CONTENTS_ATTRIBUTE = "Contents";
/*
* Constructor
*
* @param type the type to graph
* @param graphService the GraphService that will display the graph
*/
public TypeGraphTask(DataType type, GraphDisplayProvider graphService) {
super("Graph Data Type", true, false, true);
this.type = type;
if (this.type instanceof TypeDef) {
this.type = ((TypeDef) this.type).getBaseDataType();
}
this.graphTitle = "Graph of Type: " + type.getName();
this.graphService = graphService;
}
@Override
public void run(TaskMonitor monitor) throws CancelledException {
AttributedGraph graph = new AttributedGraph();
try {
if (type instanceof Pointer) {
recursePointer((Pointer) type, graph, null, monitor);
}
if (type instanceof Composite) {
recurseComposite((Composite) type, graph, null, null, monitor);
}
}
catch (CancelledException e) {
monitor.setMessage("Cancelling...");
graphTitle = graphTitle + " (partial)";
}
GraphDisplay display;
try {
display = graphService.getGraphDisplay(false, monitor);
display.setGraph(graph, graphTitle, false, monitor);
}
catch (GraphException e) {
Msg.showError(this, null, "Data Type Graph Error",
"Unexpected error while graphing: " + e.getMessage(), e);
}
}
private void recurseComposite(Composite struct, AttributedGraph graph,
AttributedVertex lastVertex, String edgeType, TaskMonitor monitor)
throws CancelledException {
AttributedVertex newVertex = new AttributedVertex(struct.getName());
newVertex.setAttribute(CONTENTS_ATTRIBUTE, struct.toString());
if (lastVertex == null) {
graph.addVertex(newVertex);
}
else {
AttributedEdge edge = graph.addEdge(lastVertex, newVertex);
if (edgeType == POINTER) {
edge.setAttribute("Color", "Blue");
}
edge.setAttribute(TYPE_ATTRIBUTE, edgeType);
if (edge.hasAttribute("Weight")) {
//did this already, don't cycle
return;
}
}
for (DataTypeComponent inner : struct.getComponents()) {
monitor.checkCanceled();
DataType dt = inner.getDataType();
if (dt instanceof TypeDef) {
dt = ((TypeDef) dt).getBaseDataType();
}
if (dt instanceof Pointer) {
recursePointer((Pointer) dt, graph, newVertex, monitor);
}
else if (dt instanceof Composite) {
recurseComposite((Composite) dt, graph, newVertex, EMBEDDED, monitor);
}
}
}
private void recursePointer(Pointer pointer, AttributedGraph graph, AttributedVertex lastVertex,
TaskMonitor monitor) throws CancelledException {
monitor.checkCanceled();
DataType ptrType = pointer.getDataType();
if (ptrType == null) {
return;
}
if (ptrType instanceof TypeDef) {
ptrType = ((TypeDef) ptrType).getBaseDataType();
}
if (ptrType instanceof Pointer) {
recursePointer((Pointer) ptrType, graph, lastVertex, monitor);
}
else if (ptrType instanceof Composite) {
recurseComposite((Composite) ptrType, graph, lastVertex, POINTER, monitor);
}
}
}

View file

@ -0,0 +1,251 @@
/* ###
* 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.app.plugin.core.datamgr.actions;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
import ghidra.graph.program.TestGraphDisplay;
import ghidra.graph.program.TestGraphService;
import ghidra.program.model.data.*;
import ghidra.service.graph.*;
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
import ghidra.util.exception.GraphException;
import ghidra.util.task.Task;
import ghidra.util.task.TaskMonitor;
public class TypeGraphTaskTest extends AbstractGhidraHeadedIntegrationTest {
private Structure base;
private Structure other;
private Pointer pointer;
private Pointer otherPointer;
private TypeDef otherTypeDef;
private TestGraphService graphService;
@Before
public void setUp() throws Exception {
base = new StructureDataType("base structure", 16);
base.insert(0, new IntegerDataType());
other = new StructureDataType("another struct", 20);
other.insert(0, new IntegerDataType());
other.insert(1, new FloatDataType());
pointer = new PointerDataType(new IntegerDataType());
otherPointer = new PointerDataType(other);
otherTypeDef = new TypedefDataType("other_t", other);
graphService = new TestGraphService();
}
@Test
public void testSimpleStructure() throws GraphException {
Task task = new TypeGraphTask(base, graphService);
task.monitoredRun(TaskMonitor.DUMMY);
TestGraphDisplay display =
(TestGraphDisplay) graphService.getGraphDisplay(true, TaskMonitor.DUMMY);
AttributedGraph graph = display.getGraph();
assertEquals(1, graph.getVertexCount());
AttributedVertex v1 = graph.getVertex(base.getName());
assertNotNull(v1);
}
@Test
public void testNestedStructure() throws GraphException {
base.insert(1, other);
Task task = new TypeGraphTask(base, graphService);
task.monitoredRun(TaskMonitor.DUMMY);
TestGraphDisplay display =
(TestGraphDisplay) graphService.getGraphDisplay(true, TaskMonitor.DUMMY);
AttributedGraph graph = display.getGraph();
assertEquals(2, graph.getVertexCount());
AttributedVertex v1 = graph.getVertex(base.getName());
assertNotNull(v1);
AttributedVertex v2 = graph.getVertex(other.getName());
assertNotNull(v2);
AttributedEdge e1 = graph.getEdge(v1, v2);
assertNotNull(e1);
assertEquals(TypeGraphTask.EMBEDDED, e1.getAttribute(TypeGraphTask.TYPE_ATTRIBUTE));
}
@Test
public void testStructureWithPointer() throws GraphException {
base.insert(1, pointer);
Task task = new TypeGraphTask(base, graphService);
task.monitoredRun(TaskMonitor.DUMMY);
TestGraphDisplay display =
(TestGraphDisplay) graphService.getGraphDisplay(true, TaskMonitor.DUMMY);
AttributedGraph graph = display.getGraph();
assertEquals(1, graph.getVertexCount());
AttributedVertex v1 = graph.getVertex(base.getName());
assertNotNull(v1);
}
@Test
public void testPointerToStructure() throws GraphException {
base.insert(1, otherPointer);
Task task = new TypeGraphTask(base, graphService);
task.monitoredRun(TaskMonitor.DUMMY);
TestGraphDisplay display =
(TestGraphDisplay) graphService.getGraphDisplay(true, TaskMonitor.DUMMY);
AttributedGraph graph = display.getGraph();
assertEquals(2, graph.getVertexCount());
AttributedVertex v1 = graph.getVertex(base.getName());
assertNotNull(v1);
AttributedVertex v2 = graph.getVertex(other.getName());
assertNotNull(v2);
AttributedEdge e1 = graph.getEdge(v1, v2);
assertNotNull(e1);
assertEquals(TypeGraphTask.POINTER, e1.getAttribute(TypeGraphTask.TYPE_ATTRIBUTE));
}
@Test
public void testEmbeddedAndPointer() throws GraphException {
base.insert(1, other);
base.insert(2, pointer);
Task task = new TypeGraphTask(base, graphService);
task.monitoredRun(TaskMonitor.DUMMY);
TestGraphDisplay display =
(TestGraphDisplay) graphService.getGraphDisplay(true, TaskMonitor.DUMMY);
AttributedGraph graph = display.getGraph();
assertEquals(2, graph.getVertexCount());
AttributedVertex v1 = graph.getVertex(base.getName());
assertNotNull(v1);
AttributedVertex v2 = graph.getVertex(other.getName());
assertNotNull(v2);
AttributedEdge e1 = graph.getEdge(v1, v2);
assertNotNull(e1);
assertEquals(TypeGraphTask.EMBEDDED, e1.getAttribute(TypeGraphTask.TYPE_ATTRIBUTE));
}
@Test
public void testPointerToPointer() throws GraphException {
Pointer pointerToPointer = new PointerDataType(otherPointer);
base.insert(1, pointerToPointer);
Task task = new TypeGraphTask(base, graphService);
task.monitoredRun(TaskMonitor.DUMMY);
TestGraphDisplay display =
(TestGraphDisplay) graphService.getGraphDisplay(true, TaskMonitor.DUMMY);
AttributedGraph graph = display.getGraph();
assertEquals(2, graph.getVertexCount());
AttributedVertex v1 = graph.getVertex(base.getName());
assertNotNull(v1);
AttributedVertex v2 = graph.getVertex(other.getName());
assertNotNull(v2);
AttributedEdge e1 = graph.getEdge(v1, v2);
assertNotNull(e1);
assertEquals(TypeGraphTask.POINTER, e1.getAttribute(TypeGraphTask.TYPE_ATTRIBUTE));
}
@Test
public void testEmbeddedTypedef() throws GraphException {
base.insert(1, otherTypeDef);
Task task = new TypeGraphTask(base, graphService);
task.monitoredRun(TaskMonitor.DUMMY);
TestGraphDisplay display =
(TestGraphDisplay) graphService.getGraphDisplay(true, TaskMonitor.DUMMY);
AttributedGraph graph = display.getGraph();
assertEquals(2, graph.getVertexCount());
AttributedVertex v1 = graph.getVertex(base.getName());
assertNotNull(v1);
AttributedVertex v2 = graph.getVertex(other.getName());
assertNotNull(v2);
AttributedEdge e1 = graph.getEdge(v1, v2);
assertNotNull(e1);
assertEquals(TypeGraphTask.EMBEDDED, e1.getAttribute(TypeGraphTask.TYPE_ATTRIBUTE));
}
@Test
public void testPointerToTypedef() throws GraphException {
Pointer typedefPtr = new PointerDataType(otherTypeDef);
base.insert(1, typedefPtr);
Task task = new TypeGraphTask(base, graphService);
task.monitoredRun(TaskMonitor.DUMMY);
TestGraphDisplay display =
(TestGraphDisplay) graphService.getGraphDisplay(true, TaskMonitor.DUMMY);
AttributedGraph graph = display.getGraph();
assertEquals(2, graph.getVertexCount());
AttributedVertex v1 = graph.getVertex(base.getName());
assertNotNull(v1);
AttributedVertex v2 = graph.getVertex(other.getName());
assertNotNull(v2);
AttributedEdge e1 = graph.getEdge(v1, v2);
assertNotNull(e1);
assertEquals(TypeGraphTask.POINTER, e1.getAttribute(TypeGraphTask.TYPE_ATTRIBUTE));
}
@Test
public void testPointerToSelf() throws GraphException {
Pointer selfPtr = new PointerDataType(base);
base.insert(1, selfPtr);
Task task = new TypeGraphTask(base, graphService);
task.monitoredRun(TaskMonitor.DUMMY);
TestGraphDisplay display =
(TestGraphDisplay) graphService.getGraphDisplay(true, TaskMonitor.DUMMY);
AttributedGraph graph = display.getGraph();
assertEquals(1, graph.getVertexCount());
AttributedVertex v1 = graph.getVertex(base.getName());
assertNotNull(v1);
AttributedEdge e1 = graph.getEdge(v1, v1);
assertNotNull(e1);
}
@Test
public void testPointerCycle() throws GraphException {
base.insert(1, otherPointer);
Pointer basePtr = new PointerDataType(base);
other.insert(1, basePtr);
Task task = new TypeGraphTask(base, graphService);
task.monitoredRun(TaskMonitor.DUMMY);
TestGraphDisplay display =
(TestGraphDisplay) graphService.getGraphDisplay(true, TaskMonitor.DUMMY);
AttributedGraph graph = display.getGraph();
assertEquals(2, graph.getVertexCount());
AttributedVertex v1 = graph.getVertex(base.getName());
assertNotNull(v1);
AttributedVertex v2 = graph.getVertex(other.getName());
assertNotNull(v2);
AttributedEdge e1 = graph.getEdge(v1, v2);
assertNotNull(e1);
AttributedEdge e2 = graph.getEdge(v2, v1);
assertNotNull(e2);
}
}