mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 09:49:23 +02:00
Merge remote-tracking branch 'origin/GP-5823_ghidragon_data_graph_improvements--SQUASHED'
This commit is contained in:
commit
84fb7f6d08
12 changed files with 275 additions and 208 deletions
|
@ -1,76 +0,0 @@
|
|||
/* ###
|
||||
* 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.debug.gui.graph.data;
|
||||
|
||||
import datagraph.AbstractDataGraphPlugin;
|
||||
import ghidra.app.context.ListingActionContext;
|
||||
import ghidra.app.plugin.PluginCategoryNames;
|
||||
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
|
||||
import ghidra.app.plugin.core.debug.event.TraceLocationPluginEvent;
|
||||
import ghidra.framework.plugintool.*;
|
||||
import ghidra.framework.plugintool.util.PluginStatus;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
|
||||
/**
|
||||
* Plugin for showing a graph of data from the listing.
|
||||
*/
|
||||
//@formatter:off
|
||||
@PluginInfo(
|
||||
status = PluginStatus.RELEASED,
|
||||
packageName = DebuggerPluginPackage.NAME,
|
||||
category = PluginCategoryNames.DEBUGGER,
|
||||
shortDescription = "Debugger Data Graph",
|
||||
description = """
|
||||
Plugin for displaying graphs of data objects in memory. From any data object in the
|
||||
listing, the user can display a graph of that data object. Initially, a graph will be shown
|
||||
with one vertex that has a scrollable view of the values in memory associated with that data.
|
||||
Also, any pointers or references from or to that data can be explored by following the
|
||||
references and creating additional vertices for the referenced code or data.
|
||||
""",
|
||||
eventsConsumed = {
|
||||
TraceLocationPluginEvent.class,
|
||||
},
|
||||
eventsProduced = {
|
||||
TraceLocationPluginEvent.class,
|
||||
}
|
||||
)
|
||||
//@formatter:on
|
||||
public class DebuggerDataGraphPlugin extends AbstractDataGraphPlugin {
|
||||
public DebuggerDataGraphPlugin(PluginTool plugintool) {
|
||||
super(plugintool);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processEvent(PluginEvent event) {
|
||||
if (event instanceof TraceLocationPluginEvent ev) {
|
||||
ProgramLocation location = ev.getLocation();
|
||||
goTo(location);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fireLocationEvent(ProgramLocation location) {
|
||||
firePluginEvent(new TraceLocationPluginEvent(getName(), location));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isGraphActionEnabled(ListingActionContext context) {
|
||||
if (!context.getNavigatable().isDynamic()) {
|
||||
return false;
|
||||
}
|
||||
return super.isGraphActionEnabled(context);
|
||||
}
|
||||
}
|
|
@ -44,6 +44,16 @@ public interface Navigatable {
|
|||
*/
|
||||
public boolean goTo(Program program, ProgramLocation location);
|
||||
|
||||
/**
|
||||
* Commands this navigatable to goto (display) the given location, using the program
|
||||
* in the location.
|
||||
* @param location the location in that program to display
|
||||
* @return true if the goto was successful
|
||||
*/
|
||||
public default boolean goTo(ProgramLocation location) {
|
||||
return goTo(location.getProgram(), location);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current location of this Navigatable
|
||||
*
|
||||
|
|
|
@ -141,7 +141,7 @@ public class DataTypeListingHover extends AbstractConfigurableHover implements L
|
|||
sb.append("<TABLE>");
|
||||
if (parent != null) {
|
||||
DataType parentType = parent.getDataType();
|
||||
sb.append(row("Parent: ", html(parentType.getDataTypePath())));
|
||||
sb.append(row("Parent: ", parentType.getDataTypePath()));
|
||||
int offset = (int) data.getAddress().subtract(parent.getAddress());
|
||||
sb.append(row("Offset: ", NumericUtilities.toHexString(offset)));
|
||||
sb.append(row("Field Name: ", nameLoc.getFieldName()));
|
||||
|
@ -149,7 +149,7 @@ public class DataTypeListingHover extends AbstractConfigurableHover implements L
|
|||
DataTypeComponent dtc = pst.getComponentAt(offset);
|
||||
String comment = dtc == null ? null : dtc.getComment();
|
||||
if (comment != null) {
|
||||
sb.append(row("Comment: ", html(comment)));
|
||||
sb.append(row("Comment: ", comment));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -160,16 +160,17 @@ public class DataTypeListingHover extends AbstractConfigurableHover implements L
|
|||
return null;
|
||||
}
|
||||
|
||||
private String row(String... cols) {
|
||||
private String row(Object... cols) {
|
||||
StringBuilder sb = new StringBuilder("<TR>");
|
||||
for (String col : cols) {
|
||||
sb.append("<TD>").append(col).append("</TD>");
|
||||
for (Object col : cols) {
|
||||
String escaped = escapeHtml(col);
|
||||
sb.append("<TD>").append(escaped).append("</TD>");
|
||||
}
|
||||
sb.append("</TR>");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String html(Object obj) {
|
||||
private String escapeHtml(Object obj) {
|
||||
return obj == null ? "" : HTMLUtilities.friendlyEncodeHTML(obj.toString());
|
||||
}
|
||||
|
||||
|
@ -182,10 +183,12 @@ public class DataTypeListingHover extends AbstractConfigurableHover implements L
|
|||
if (StringDataInstance.isString(dataInstance)) {
|
||||
StringDataInstance sdi = StringDataInstance.getStringDataInstance(dataInstance);
|
||||
if (sdi.isShowTranslation()) {
|
||||
result += String.format("<br>Original value: %s", html(sdi.getStringValue()));
|
||||
String escaped = escapeHtml(sdi.getStringValue());
|
||||
result += String.format("<br>Original value: %s", escaped);
|
||||
}
|
||||
if (!sdi.isShowTranslation() && sdi.getTranslatedValue() != null) {
|
||||
result += String.format("<br>Translated value: %s", html(sdi.getTranslatedValue()));
|
||||
String escaped = escapeHtml(sdi.getTranslatedValue());
|
||||
result += String.format("<br>Translated value: %s", escaped);
|
||||
}
|
||||
if (sdi.isMissingNullTerminator()) {
|
||||
result += "<br>Missing NULL terminator.";
|
||||
|
|
|
@ -141,6 +141,10 @@
|
|||
|
||||
<LI><A name="Delete_Vertex"><IMG alt="" src="Icons.CLOSE_ICON"> <B>Delete Vertex</B> - Removes this vertex and all vertices
|
||||
that descend from this vertex.</A></LI>
|
||||
|
||||
<LI><A name="Reset_Location"><IMG alt="" src="Icons.REFRESH_ICON"> <B>Restore Location</B> - Moves the vertex
|
||||
to its preferred location (only shows up if the vertex was manually moved).</A></LI>
|
||||
|
||||
</UL>
|
||||
|
||||
<P><A name="Popups">The following popup actions are are available depending on where the
|
||||
|
@ -167,6 +171,9 @@
|
|||
<LI><A name="Delete_Selected"><B>Delete Selected Vertices</B> - Deletes the selected
|
||||
vertices and any descendants vertices (vertices that were discovered via exploring from
|
||||
that vertex.)</A></LI>
|
||||
|
||||
<LI><A name="Show_Popups"><B>Show Popups</B> - If selected, hovering over data rows in a data
|
||||
vertex will show additional information for the data in that row.</A></LI>
|
||||
</UL>
|
||||
</BLOCKQUOTE>
|
||||
<H3>Selecting Vertices</H3>
|
||||
|
@ -227,6 +234,7 @@
|
|||
vertices will show more information for each row in the display. In compact mode, a data
|
||||
row will generally show the field name and its value. In expanded mode, a data row will
|
||||
generally show the datatype, field name, and its value.</A></LI>
|
||||
|
||||
</UL>
|
||||
</BLOCKQUOTE
|
||||
<H2>Standard Graph Features and Actions</H2>
|
||||
|
|
|
@ -1,84 +0,0 @@
|
|||
/* ###
|
||||
* 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 datagraph;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import docking.action.builder.ActionBuilder;
|
||||
import ghidra.app.context.ListingActionContext;
|
||||
import ghidra.app.plugin.ProgramPlugin;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.listing.Data;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.HelpLocation;
|
||||
|
||||
/**
|
||||
* Base class for plugins that show a graph of data from program.
|
||||
*/
|
||||
|
||||
public abstract class AbstractDataGraphPlugin extends ProgramPlugin {
|
||||
private Set<DataGraphProvider> activeProviders = new HashSet<>();
|
||||
|
||||
public AbstractDataGraphPlugin(PluginTool plugintool) {
|
||||
super(plugintool);
|
||||
createActions();
|
||||
}
|
||||
|
||||
public void goTo(ProgramLocation location) {
|
||||
activeProviders.forEach(p -> p.goTo(location));
|
||||
}
|
||||
|
||||
private void createActions() {
|
||||
|
||||
new ActionBuilder("Display Data Graph", getName())
|
||||
.popupMenuPath("Data", "Display Data Graph")
|
||||
.keyBinding("ctrl G")
|
||||
.helpLocation(new HelpLocation("DataGraphPlugin", "Data_Graph"))
|
||||
.withContext(ListingActionContext.class)
|
||||
.enabledWhen(this::isGraphActionEnabled)
|
||||
.onAction(this::showDataGraph)
|
||||
.buildAndInstall(tool);
|
||||
}
|
||||
|
||||
protected boolean isGraphActionEnabled(ListingActionContext context) {
|
||||
return context.getCodeUnit() instanceof Data;
|
||||
}
|
||||
|
||||
private void showDataGraph(ListingActionContext context) {
|
||||
Data data = (Data) context.getCodeUnit();
|
||||
// the data from the context may be an internal sub-data, we want the outermost data.
|
||||
data = getTopLevelData(data);
|
||||
DataGraphProvider provider = new DataGraphProvider(this, data);
|
||||
activeProviders.add(provider);
|
||||
tool.showComponentProvider(provider, true);
|
||||
}
|
||||
|
||||
private Data getTopLevelData(Data data) {
|
||||
Data parent = data.getParent();
|
||||
while (parent != null) {
|
||||
data = parent;
|
||||
parent = data.getParent();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
void removeProvider(DataGraphProvider provider) {
|
||||
activeProviders.remove(provider);
|
||||
}
|
||||
|
||||
public abstract void fireLocationEvent(ProgramLocation location);
|
||||
}
|
|
@ -15,13 +15,22 @@
|
|||
*/
|
||||
package datagraph;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import docking.action.builder.ActionBuilder;
|
||||
import ghidra.app.CorePluginPackage;
|
||||
import ghidra.app.context.ListingActionContext;
|
||||
import ghidra.app.events.AbstractLocationPluginEvent;
|
||||
import ghidra.app.events.ProgramLocationPluginEvent;
|
||||
import ghidra.app.plugin.PluginCategoryNames;
|
||||
import ghidra.app.plugin.ProgramPlugin;
|
||||
import ghidra.framework.options.SaveState;
|
||||
import ghidra.framework.plugintool.*;
|
||||
import ghidra.framework.plugintool.util.PluginStatus;
|
||||
import ghidra.program.model.listing.Data;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.HelpLocation;
|
||||
|
||||
/**
|
||||
* Plugin for showing a graph of data from the listing.
|
||||
|
@ -47,29 +56,90 @@ import ghidra.program.util.ProgramLocation;
|
|||
}
|
||||
)
|
||||
//@formatter:on
|
||||
public class DataGraphPlugin extends AbstractDataGraphPlugin {
|
||||
public class DataGraphPlugin extends ProgramPlugin {
|
||||
private static final String NAVIGATE_IN = "Navigate In";
|
||||
private static final String NAVIGATE_OUT = "Navigate Out";
|
||||
private static final String COMPACT_FORMAT = "Compact Format";
|
||||
private static final String SHOW_POPUPS = "Show Popups";
|
||||
private Set<DataGraphProvider> activeProviders = new HashSet<>();
|
||||
private DegSharedConfig sharedConfig = new DegSharedConfig();
|
||||
|
||||
public DataGraphPlugin(PluginTool plugintool) {
|
||||
super(plugintool);
|
||||
createActions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processEvent(PluginEvent event) {
|
||||
if (event instanceof ProgramLocationPluginEvent ev) {
|
||||
if (event instanceof AbstractLocationPluginEvent ev) {
|
||||
ProgramLocation location = ev.getLocation();
|
||||
goTo(location);
|
||||
setLocation(location);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fireLocationEvent(ProgramLocation location) {
|
||||
firePluginEvent(new ProgramLocationPluginEvent(getName(), location, location.getProgram()));
|
||||
/**
|
||||
* Pass incoming tool location events to each active provider.
|
||||
* @param location the new tool location
|
||||
*/
|
||||
public void setLocation(ProgramLocation location) {
|
||||
activeProviders.forEach(p -> p.setLocation(location));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readConfigState(SaveState saveState) {
|
||||
sharedConfig.setNavigateIn(saveState.getBoolean(NAVIGATE_IN, false));
|
||||
sharedConfig.setNavigateOut(saveState.getBoolean(NAVIGATE_OUT, true));
|
||||
sharedConfig.setCompactFormat(saveState.getBoolean(COMPACT_FORMAT, true));
|
||||
sharedConfig.setShowPopups(saveState.getBoolean(SHOW_POPUPS, true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeConfigState(SaveState saveState) {
|
||||
saveState.putBoolean(NAVIGATE_IN, sharedConfig.isNavigateIn());
|
||||
saveState.putBoolean(NAVIGATE_OUT, sharedConfig.isNavigateOut());
|
||||
saveState.putBoolean(COMPACT_FORMAT, sharedConfig.useCompactFormat());
|
||||
saveState.putBoolean(SHOW_POPUPS, sharedConfig.isShowPopups());
|
||||
}
|
||||
|
||||
private void createActions() {
|
||||
|
||||
new ActionBuilder("Display Data Graph", getName())
|
||||
.menuPath("&Graph", "Data")
|
||||
.menuGroup("Graph", "Data")
|
||||
.popupMenuPath("Data", "Display Data Graph")
|
||||
.keyBinding("ctrl G")
|
||||
.helpLocation(new HelpLocation("DataGraphPlugin", "Data_Graph"))
|
||||
.withContext(ListingActionContext.class)
|
||||
.enabledWhen(this::isGraphActionEnabled)
|
||||
.onAction(this::showDataGraph)
|
||||
.buildAndInstall(tool);
|
||||
}
|
||||
|
||||
protected boolean isGraphActionEnabled(ListingActionContext context) {
|
||||
if (context.getNavigatable().isDynamic()) {
|
||||
return false;
|
||||
return context.getCodeUnit() instanceof Data;
|
||||
}
|
||||
return super.isGraphActionEnabled(context);
|
||||
|
||||
private void showDataGraph(ListingActionContext context) {
|
||||
Data data = (Data) context.getCodeUnit();
|
||||
// the data from the context may be an internal sub-data, we want the outermost data.
|
||||
data = getTopLevelData(data);
|
||||
DataGraphProvider provider =
|
||||
new DataGraphProvider(this, context.getNavigatable(), data, sharedConfig);
|
||||
activeProviders.add(provider);
|
||||
tool.showComponentProvider(provider, true);
|
||||
}
|
||||
|
||||
private Data getTopLevelData(Data data) {
|
||||
Data parent = data.getParent();
|
||||
while (parent != null) {
|
||||
data = parent;
|
||||
parent = data.getParent();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
void removeProvider(DataGraphProvider provider) {
|
||||
activeProviders.remove(provider);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -29,10 +29,12 @@ import docking.action.ToggleDockingAction;
|
|||
import docking.action.builder.ActionBuilder;
|
||||
import docking.action.builder.ToggleActionBuilder;
|
||||
import generic.theme.GIcon;
|
||||
import ghidra.app.nav.Navigatable;
|
||||
import ghidra.graph.VisualGraphComponentProvider;
|
||||
import ghidra.graph.viewer.*;
|
||||
import ghidra.graph.viewer.GraphComponent.SatellitePosition;
|
||||
import ghidra.graph.viewer.event.mouse.VertexMouseInfo;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Data;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
|
@ -52,24 +54,28 @@ public class DataGraphProvider
|
|||
private static final GIcon RESET_ICON = new GIcon("icon.plugin.datagraph.action.viewer.reset");
|
||||
private static final String NAME = "Data Graph";
|
||||
|
||||
private AbstractDataGraphPlugin plugin;
|
||||
private DataGraphPlugin plugin;
|
||||
private JPanel mainPanel;
|
||||
|
||||
private DegController controller;
|
||||
private ToggleDockingAction navagateInAction;
|
||||
private ToggleDockingAction navagateOutAction;
|
||||
private ToggleDockingAction expandedFormatAction;
|
||||
private Navigatable navigatable;
|
||||
private ToggleDockingAction togglePopups;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param plugin the DataGraphPlugin
|
||||
* @param data the initial data object to display in the graph.
|
||||
*/
|
||||
public DataGraphProvider(AbstractDataGraphPlugin plugin, Data data) {
|
||||
public DataGraphProvider(DataGraphPlugin plugin, Navigatable navigatable, Data data,
|
||||
DegSharedConfig sharedConfig) {
|
||||
super(plugin.getTool(), NAME, plugin.getName());
|
||||
this.plugin = plugin;
|
||||
controller = new DegController(this, data);
|
||||
createActions();
|
||||
this.navigatable = navigatable;
|
||||
controller = new DegController(this, data, sharedConfig);
|
||||
createActions(sharedConfig);
|
||||
setTransient();
|
||||
|
||||
buildComponent();
|
||||
|
@ -77,6 +83,14 @@ public class DataGraphProvider
|
|||
addSatelliteFeature(false, SatellitePosition.LOWER_LEFT);
|
||||
|
||||
setHelpLocation(new HelpLocation("DataGraphPlugin", "DataGraphPlugin"));
|
||||
updateSubTitle();
|
||||
}
|
||||
|
||||
public void updateSubTitle() {
|
||||
Program program = controller.getProgram();
|
||||
DataExplorationGraph graph = controller.getGraph();
|
||||
Address address = graph.getRoot().getAddress();
|
||||
setSubTitle(program.getName() + " @ " + address);
|
||||
}
|
||||
|
||||
private void buildComponent() {
|
||||
|
@ -141,7 +155,7 @@ public class DataGraphProvider
|
|||
return controller;
|
||||
}
|
||||
|
||||
private void createActions() {
|
||||
private void createActions(DegSharedConfig sharedConfig) {
|
||||
new ActionBuilder("Select Home Vertex", plugin.getName())
|
||||
.toolBarIcon(Icons.HOME_ICON)
|
||||
.toolBarGroup("A")
|
||||
|
@ -159,6 +173,7 @@ public class DataGraphProvider
|
|||
.toolBarIcon(DETAILS_ICON)
|
||||
.toolBarGroup("A")
|
||||
.description("Show Expanded information in data vertices.")
|
||||
.selected(!sharedConfig.useCompactFormat())
|
||||
.helpLocation(new HelpLocation("DataGraphPlugin", "Expanded_Format"))
|
||||
.onAction(c -> controller.setCompactFormat(!expandedFormatAction.isSelected()))
|
||||
.buildAndInstallLocal(this);
|
||||
|
@ -169,6 +184,7 @@ public class DataGraphProvider
|
|||
.toolBarIcon(Icons.NAVIGATE_ON_INCOMING_EVENT_ICON)
|
||||
.toolBarGroup("B")
|
||||
.description("Attemps to select vertex corresponding to tool location changes.")
|
||||
.selected(sharedConfig.isNavigateIn())
|
||||
.helpLocation(new HelpLocation("DataGraphPlugin", "Navigate_In"))
|
||||
.onAction(c -> controller.setNavigateIn(navagateInAction.isSelected()))
|
||||
.buildAndInstallLocal(this);
|
||||
|
@ -180,11 +196,22 @@ public class DataGraphProvider
|
|||
.sharedKeyBinding()
|
||||
.description(
|
||||
"Selecting vetices or locations inside a vertex sends navigates the tool.")
|
||||
.selected(sharedConfig.isNavigateOut())
|
||||
.helpLocation(new HelpLocation("DataGraphPlugin", "Navigate_Out"))
|
||||
.onAction(c -> controller.setNavigateOut(navagateOutAction.isSelected()))
|
||||
.selected(true)
|
||||
.buildAndInstallLocal(this);
|
||||
|
||||
togglePopups = new ToggleActionBuilder("Display Popup Windows", plugin.getName())
|
||||
.popupMenuPath("Display Popup Windows")
|
||||
.description("Toggles whether or not to show tooltips")
|
||||
.selected(sharedConfig.isShowPopups())
|
||||
.helpLocation(new HelpLocation("DataGraphPlugin", "Show_Popups"))
|
||||
.withContext(DegContext.class)
|
||||
.popupWhen(c -> c.getVertex() == null)
|
||||
.onAction(c -> controller.setPopupsVisible(togglePopups.isSelected()))
|
||||
.buildAndInstallLocal(this);
|
||||
|
||||
new ActionBuilder("Incoming References", plugin.getName())
|
||||
.popupMenuPath("Add All Incoming References")
|
||||
.popupMenuGroup("A", "2")
|
||||
|
@ -271,8 +298,8 @@ public class DataGraphProvider
|
|||
return !v.isRoot();
|
||||
}
|
||||
|
||||
void goTo(ProgramLocation location) {
|
||||
controller.locationChanged(location);
|
||||
void setLocation(ProgramLocation location) {
|
||||
controller.setLocation(location);
|
||||
}
|
||||
|
||||
private boolean canExpandRecursively(DegContext context) {
|
||||
|
@ -289,8 +316,7 @@ public class DataGraphProvider
|
|||
}
|
||||
|
||||
public void navigateOut(ProgramLocation location) {
|
||||
plugin.fireLocationEvent(location);
|
||||
|
||||
navigatable.goTo(location);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
/* ###
|
||||
* 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 datagraph;
|
||||
|
||||
// Simple storage of shared data graph configuration states. If any provider changes any of these,
|
||||
// then that will be the value going forward. In other words, the last one in wins.
|
||||
public class DegSharedConfig {
|
||||
private boolean navigateIn = false;
|
||||
private boolean navigateOut = true;
|
||||
private boolean showPopups = true;
|
||||
private boolean useCompactFormat = true;
|
||||
|
||||
public boolean isNavigateIn() {
|
||||
return navigateIn;
|
||||
}
|
||||
|
||||
public void setNavigateIn(boolean navigateIn) {
|
||||
this.navigateIn = navigateIn;
|
||||
}
|
||||
|
||||
public boolean isNavigateOut() {
|
||||
return navigateOut;
|
||||
}
|
||||
|
||||
public void setNavigateOut(boolean navigateOut) {
|
||||
this.navigateOut = navigateOut;
|
||||
}
|
||||
|
||||
public boolean isShowPopups() {
|
||||
return showPopups;
|
||||
}
|
||||
|
||||
public void setShowPopups(boolean showPopups) {
|
||||
this.showPopups = showPopups;
|
||||
}
|
||||
|
||||
public boolean useCompactFormat() {
|
||||
return useCompactFormat;
|
||||
}
|
||||
|
||||
public void setCompactFormat(boolean useCompactFormat) {
|
||||
this.useCompactFormat = useCompactFormat;
|
||||
}
|
||||
|
||||
}
|
|
@ -28,6 +28,7 @@ import java.util.stream.Collectors;
|
|||
import javax.swing.JComponent;
|
||||
|
||||
import datagraph.DataGraphProvider;
|
||||
import datagraph.DegSharedConfig;
|
||||
import datagraph.data.graph.DegVertex.DegVertexStatus;
|
||||
import edu.uci.ics.jung.visualization.control.AbstractGraphMousePlugin;
|
||||
import ghidra.app.util.XReferenceUtils;
|
||||
|
@ -43,6 +44,7 @@ import ghidra.program.model.address.Address;
|
|||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.symbol.Reference;
|
||||
import ghidra.program.model.symbol.ReferenceManager;
|
||||
import ghidra.program.util.AddressFieldLocation;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
|
@ -62,14 +64,17 @@ public class DegController implements DomainObjectListener {
|
|||
private boolean navigateIn = false;
|
||||
private boolean compactFormat = true;
|
||||
private DataGraphProvider provider;
|
||||
private DegSharedConfig sharedConfig;
|
||||
|
||||
/**
|
||||
* Constructs a new data exploration graph controller.
|
||||
* @param provider The data graph provider that created this controller
|
||||
* @param data the initial data to display in the graph
|
||||
* @param sharedConfig the shared data graph configuration state
|
||||
*/
|
||||
public DegController(DataGraphProvider provider, Data data) {
|
||||
public DegController(DataGraphProvider provider, Data data, DegSharedConfig sharedConfig) {
|
||||
this.provider = provider;
|
||||
this.sharedConfig = sharedConfig;
|
||||
this.program = data.getProgram();
|
||||
view = new DegGraphView();
|
||||
DegVertex root = new DataDegVertex(this, data, null, true);
|
||||
|
@ -88,8 +93,9 @@ public class DegController implements DomainObjectListener {
|
|||
*/
|
||||
public void navigateOut(Address address, int[] componentPath) {
|
||||
if (navigateOut) {
|
||||
// Using an address field location skips past pre or plate comments
|
||||
ProgramLocation location =
|
||||
new ProgramLocation(program, address, address, componentPath, null, 0, 0, 0);
|
||||
new AddressFieldLocation(program, address, componentPath, address.toString(), 0);
|
||||
provider.navigateOut(location);
|
||||
}
|
||||
}
|
||||
|
@ -226,6 +232,7 @@ public class DegController implements DomainObjectListener {
|
|||
public void orientAround(DegVertex newRoot) {
|
||||
graph.setRoot(newRoot);
|
||||
relayoutGraphAndCenter(newRoot);
|
||||
provider.updateSubTitle();
|
||||
}
|
||||
|
||||
public VisualGraphView<DegVertex, DegEdge, DataExplorationGraph> getView() {
|
||||
|
@ -247,6 +254,7 @@ public class DegController implements DomainObjectListener {
|
|||
*/
|
||||
public void setNavigateOut(boolean b) {
|
||||
navigateOut = b;
|
||||
sharedConfig.setNavigateOut(b);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -256,6 +264,7 @@ public class DegController implements DomainObjectListener {
|
|||
*/
|
||||
public void setNavigateIn(boolean b) {
|
||||
navigateIn = b;
|
||||
sharedConfig.setNavigateIn(b);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -270,6 +279,12 @@ public class DegController implements DomainObjectListener {
|
|||
}
|
||||
});
|
||||
relayoutGraph();
|
||||
sharedConfig.setCompactFormat(b);
|
||||
}
|
||||
|
||||
public void setPopupsVisible(boolean b) {
|
||||
view.setPopupsVisible(b);
|
||||
sharedConfig.setShowPopups(b);
|
||||
}
|
||||
|
||||
public boolean isCompactFormat() {
|
||||
|
@ -330,7 +345,7 @@ public class DegController implements DomainObjectListener {
|
|||
* graph will select that vertex.
|
||||
* @param location the new location for the tool
|
||||
*/
|
||||
public void locationChanged(ProgramLocation location) {
|
||||
public void setLocation(ProgramLocation location) {
|
||||
if (!navigateIn) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -34,7 +34,10 @@ import docking.action.DockingActionIf;
|
|||
import docking.widgets.trable.GTrable;
|
||||
import docking.widgets.trable.GTrableColumnModel;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.listing.Data;
|
||||
import ghidra.util.HTMLUtilities;
|
||||
import ghidra.util.NumericUtilities;
|
||||
import ghidra.util.datastruct.Range;
|
||||
import resources.Icons;
|
||||
|
||||
|
@ -119,7 +122,34 @@ public class DataVertexPanel extends JPanel {
|
|||
JComponent jComponent = (JComponent) source;
|
||||
return jComponent.getToolTipText();
|
||||
}
|
||||
return null;
|
||||
int row = gTrable.getRow(event.getPoint());
|
||||
DataRowObject rowObject = model.getRow(row);
|
||||
Data data = rowObject.getData();
|
||||
StringBuilder sb = new StringBuilder(HTMLUtilities.HTML);
|
||||
sb.append("<TABLE>");
|
||||
DataType dataType = data.getDataType();
|
||||
Address address = data.getAddress();
|
||||
int rootOffset = data.getRootOffset();
|
||||
|
||||
sb.append(row("Address: ", address.toString()));
|
||||
sb.append(row("Offset: ", NumericUtilities.toHexString(rootOffset)));
|
||||
sb.append(row("Data Type: ", dataType.getName()));
|
||||
sb.append("</TABLE>");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String row(Object... cols) {
|
||||
StringBuilder sb = new StringBuilder("<TR>");
|
||||
for (Object col : cols) {
|
||||
String escaped = escapeHtml(col);
|
||||
sb.append("<TD>").append(escaped).append("</TD>");
|
||||
}
|
||||
sb.append("</TR>");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String escapeHtml(Object obj) {
|
||||
return obj == null ? "" : HTMLUtilities.friendlyEncodeHTML(obj.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -185,54 +185,57 @@ public class ProgramGraphPlugin extends ProgramPlugin
|
|||
|
||||
private void createActions() {
|
||||
|
||||
new ActionBuilder("Graph Block Flow", getName()).menuPath(MENU_GRAPH, "&Block Flow")
|
||||
.menuGroup(MENU_GRAPH, "A")
|
||||
new ActionBuilder("Graph Block Flow", getName())
|
||||
.menuPath(MENU_GRAPH, "&Block Flow")
|
||||
.menuGroup("Code Graph", "A")
|
||||
.onAction(c -> graphBlockFlow())
|
||||
.enabledWhen(this::canGraph)
|
||||
.buildAndInstall(tool);
|
||||
|
||||
new ActionBuilder("Graph Code Flow", getName()).menuPath(MENU_GRAPH, "C&ode Flow")
|
||||
.menuGroup(MENU_GRAPH, "B")
|
||||
new ActionBuilder("Graph Code Flow", getName())
|
||||
.menuPath(MENU_GRAPH, "C&ode Flow")
|
||||
.menuGroup("Code Graph", "B")
|
||||
.onAction(c -> graphCodeFlow())
|
||||
.enabledWhen(this::canGraph)
|
||||
.buildAndInstall(tool);
|
||||
|
||||
new ActionBuilder("Graph Calls Using Default Model", getName())
|
||||
.menuPath(MENU_GRAPH, "&Calls")
|
||||
.menuGroup(MENU_GRAPH, "C")
|
||||
.menuGroup("Code Graph", "C")
|
||||
.onAction(c -> createDefaultCallGraph())
|
||||
.enabledWhen(this::canGraph)
|
||||
.buildAndInstall(tool);
|
||||
|
||||
tool.setMenuGroup(new String[] { MENU_GRAPH, "Data" }, "Graph", "Data");
|
||||
tool.setMenuGroup(new String[] { MENU_GRAPH, "Data References" }, "Graph", "Data");
|
||||
HelpLocation helpLoc = new HelpLocation(getName(), "Data_Reference_Graph");
|
||||
|
||||
new ActionBuilder("Graph To/From Data References", getName())
|
||||
.menuPath(MENU_GRAPH, "Data", "To/From &References")
|
||||
.menuGroup(MENU_GRAPH, "Data")
|
||||
.menuPath(MENU_GRAPH, "Data References", "To/From &References")
|
||||
.menuGroup("Graph", "Data")
|
||||
.helpLocation(helpLoc)
|
||||
.onAction(c -> graphDataReferences())
|
||||
.enabledWhen(this::canGraph)
|
||||
.buildAndInstall(tool);
|
||||
|
||||
new ActionBuilder("Graph To Data References", getName())
|
||||
.menuPath(MENU_GRAPH, "Data", "&To References")
|
||||
.menuGroup(MENU_GRAPH, "Data")
|
||||
.menuPath(MENU_GRAPH, "Data References", "&To References")
|
||||
.menuGroup("Graph", "Data")
|
||||
.helpLocation(helpLoc)
|
||||
.onAction(c -> graphToDataReferences())
|
||||
.enabledWhen(this::canGraph)
|
||||
.buildAndInstall(tool);
|
||||
|
||||
new ActionBuilder("Graph From Data References", getName())
|
||||
.menuPath(MENU_GRAPH, "Data", "&From References")
|
||||
.menuGroup(MENU_GRAPH, "Data")
|
||||
.menuPath(MENU_GRAPH, "Data References", "&From References")
|
||||
.menuGroup("Graph", "Data")
|
||||
.helpLocation(helpLoc)
|
||||
.onAction(c -> graphFromDataReferences())
|
||||
.enabledWhen(this::canGraph)
|
||||
.buildAndInstall(tool);
|
||||
|
||||
reuseGraphAction =
|
||||
new ToggleActionBuilder("Reuse Graph", getName()).menuPath(MENU_GRAPH, "Reuse Graph")
|
||||
new ToggleActionBuilder("Reuse Graph", getName())
|
||||
.menuPath(MENU_GRAPH, "Reuse Graph")
|
||||
.menuGroup("Graph Options")
|
||||
.selected(reuseGraph)
|
||||
.onAction(c -> reuseGraph = reuseGraphAction.isSelected())
|
||||
|
@ -240,7 +243,8 @@ public class ProgramGraphPlugin extends ProgramPlugin
|
|||
.buildAndInstall(tool);
|
||||
|
||||
appendGraphAction =
|
||||
new ToggleActionBuilder("Append Graph", getName()).menuPath(MENU_GRAPH, "Append Graph")
|
||||
new ToggleActionBuilder("Append Graph", getName())
|
||||
.menuPath(MENU_GRAPH, "Append Graph")
|
||||
.menuGroup("Graph Options")
|
||||
.selected(false)
|
||||
.onAction(c -> updateAppendAndReuseGraph())
|
||||
|
@ -297,13 +301,12 @@ public class ProgramGraphPlugin extends ProgramPlugin
|
|||
subUsingGraphActions.add(action);
|
||||
}
|
||||
|
||||
tool.setMenuGroup(new String[] { MENU_GRAPH, "Calls Using Model" }, "Graph", "C");
|
||||
tool.setMenuGroup(new String[] { MENU_GRAPH, "Calls Using Model" }, "Code Graph", "D");
|
||||
}
|
||||
|
||||
private DockingAction buildGraphActionWithModel(String blockModelName, HelpLocation helpLoc) {
|
||||
return new ActionBuilder("Graph Calls using " + blockModelName, getName())
|
||||
.menuPath(MENU_GRAPH, "Calls Using Model", blockModelName)
|
||||
.menuGroup(MENU_GRAPH, "C")
|
||||
.helpLocation(helpLoc)
|
||||
.onAction(c -> createCallGraphUsing(blockModelName))
|
||||
.enabledWhen(this::canGraph)
|
||||
|
|
|
@ -370,6 +370,10 @@ public class GTrable<T> extends JComponent
|
|||
columnModel.setWidth(width);
|
||||
}
|
||||
|
||||
public int getRow(Point p) {
|
||||
return p.y / rowHeight;
|
||||
}
|
||||
|
||||
private void notifySelectedRowConsumers() {
|
||||
for (Consumer<Integer> consumer : selectedRowConsumers) {
|
||||
consumer.accept(selectedRow);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue