mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-22 20:22:44 +00:00
GP-500: Added Memview (address x time object plot)
This commit is contained in:
parent
8fa549119d
commit
ea87c4e063
@ -0,0 +1,284 @@
|
||||
|
||||
/* ###
|
||||
* 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.
|
||||
*/
|
||||
/* ###
|
||||
* 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.
|
||||
*/
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
import com.sun.jna.Pointer;
|
||||
|
||||
import agent.dbgeng.dbgeng.DebugClient;
|
||||
import agent.dbgeng.dbgeng.DebugControl;
|
||||
import agent.dbgmodel.dbgmodel.DbgModel;
|
||||
import agent.dbgmodel.dbgmodel.bridge.HostDataModelAccess;
|
||||
import agent.dbgmodel.dbgmodel.main.ModelMethod;
|
||||
import agent.dbgmodel.dbgmodel.main.ModelObject;
|
||||
import agent.dbgmodel.impl.dbgmodel.bridge.HDMAUtil;
|
||||
import ghidra.app.plugin.core.debug.gui.memview.*;
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.trace.model.memory.TraceMemoryFlag;
|
||||
import ghidra.util.Swing;
|
||||
|
||||
public class PopulateMemviewLocal extends GhidraScript {
|
||||
|
||||
private Language lang;
|
||||
|
||||
private AddressSpace defaultSpace;
|
||||
|
||||
private HostDataModelAccess access;
|
||||
private DebugClient client;
|
||||
private DebugControl control;
|
||||
private HDMAUtil util;
|
||||
|
||||
private Map<String, MemoryBox> boxes = new HashMap<String, MemoryBox>();
|
||||
private Set<Long> eventSnaps = new HashSet<Long>();
|
||||
private MemviewService memview;
|
||||
|
||||
/**
|
||||
* Create an address in the processor's (x86_64) default space.
|
||||
*
|
||||
* @param offset the byte offset
|
||||
* @return the address
|
||||
*/
|
||||
protected Address addr(long offset) {
|
||||
return defaultSpace.getAddress(offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an address range in the processor's default space.
|
||||
*
|
||||
* @param min the minimum byte offset
|
||||
* @param max the maximum (inclusive) byte offset
|
||||
* @return the range
|
||||
*/
|
||||
protected AddressRange rng(long min, long max) {
|
||||
return new AddressRangeImpl(addr(min), addr(max));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a register by name
|
||||
*
|
||||
* @param name the name
|
||||
* @return the register
|
||||
*/
|
||||
protected Register reg(String name) {
|
||||
return lang.getRegister(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void run() throws Exception {
|
||||
|
||||
memview = state.getTool().getService(MemviewService.class);
|
||||
if (memview == null) {
|
||||
throw new RuntimeException("Unable to find DebuggerMemviewPlugin");
|
||||
}
|
||||
|
||||
access = DbgModel.debugCreate();
|
||||
client = access.getClient();
|
||||
control = client.getControl();
|
||||
util = new HDMAUtil(access);
|
||||
|
||||
File f = askFile("Trace", "Load");
|
||||
|
||||
lang = currentProgram.getLanguage();
|
||||
defaultSpace = lang.getAddressFactory().getDefaultAddressSpace();
|
||||
|
||||
client.openDumpFileWide(f.getAbsolutePath());
|
||||
control.waitForEvent();
|
||||
|
||||
List<ModelObject> children = util.getElements(
|
||||
List.of("Debugger", "State", "DebuggerVariables", "curprocess", "TTD", "Events"));
|
||||
|
||||
Map<String, ModelObject> maxPos = util.getAttributes(List.of("Debugger", "State",
|
||||
"DebuggerVariables", "curprocess", "TTD", "Lifetime", "MaxPosition"));
|
||||
Long max = (Long) maxPos.get("Sequence").getValue();
|
||||
|
||||
for (ModelObject event : children) {
|
||||
Map<String, ModelObject> eventMap = event.getKeyValueMap();
|
||||
ModelObject pos = eventMap.get("Position");
|
||||
ModelObject seq = pos.getKeyValue("Sequence");
|
||||
//ModelObject step = pos.getKeyValue("Steps");
|
||||
ModelObject type = eventMap.get("Type");
|
||||
String display = type.getValueString();
|
||||
Long snap = (Long) seq.getValue();
|
||||
if (display.contains("ModuleLoaded") || display.contains("ModuleUnloaded")) {
|
||||
ModelObject module = eventMap.get("Module");
|
||||
Map<String, ModelObject> moduleMap = module.getKeyValueMap();
|
||||
ModelObject name = moduleMap.get("Name");
|
||||
ModelObject address = moduleMap.get("Address");
|
||||
ModelObject size = moduleMap.get("Size");
|
||||
String moduleId = name.getValueString();
|
||||
display += " " + moduleId;
|
||||
//Address base = currentProgram.getAddressFactory().getAddress(address.getValueString());
|
||||
if (display.contains("ModuleLoaded")) {
|
||||
Long start = (Long) address.getValue();
|
||||
Long sz = (Long) size.getValue();
|
||||
AddressRange rng = rng(start, start + sz - 1);
|
||||
addLoadedModule(moduleId, moduleId, Range.atLeast(snap), rng);
|
||||
//addRegion(moduleId, Range.atLeast(snap), rng, TraceMemoryFlag.READ,
|
||||
// TraceMemoryFlag.WRITE, TraceMemoryFlag.EXECUTE);
|
||||
}
|
||||
else {
|
||||
markModuleClosed(moduleId, snap);
|
||||
}
|
||||
}
|
||||
else if (display.contains("ThreadCreated") || display.contains("ThreadTerminated")) {
|
||||
ModelObject thread = eventMap.get("Thread");
|
||||
//ModelObject uid = thread.getKeyValue("UniqueId");
|
||||
ModelObject id = thread.getKeyValue("Id");
|
||||
String threadId = id.getValueString();
|
||||
int iid = Integer.parseInt(threadId, 16);
|
||||
AddressRange rng = rng(iid, iid + 1);
|
||||
display += " " + threadId;
|
||||
if (display.contains("ThreadCreated")) {
|
||||
addThread("Thread " + threadId, Range.atLeast(snap), rng);
|
||||
}
|
||||
else {
|
||||
markThreadClosed(threadId, snap);
|
||||
}
|
||||
}
|
||||
if (snap < 0) {
|
||||
snap = ++max;
|
||||
}
|
||||
//timeManager.getSnapshot(snap, true).setDescription(display);
|
||||
eventSnaps.add(snap);
|
||||
}
|
||||
|
||||
for (Long snap : eventSnaps) {
|
||||
control.execute("!tt " + Long.toHexString(snap) + ":0");
|
||||
control.waitForEvent();
|
||||
|
||||
List<ModelObject> modelThreads = util.getElements(
|
||||
List.of("Debugger", "State", "DebuggerVariables", "curprocess", "Threads"));
|
||||
Map<String, ModelObject> modelThreadMap = new HashMap<String, ModelObject>();
|
||||
for (ModelObject obj : modelThreads) {
|
||||
modelThreadMap.put(obj.getSearchKey(), obj);
|
||||
}
|
||||
}
|
||||
|
||||
ModelObject currentSession = util.getCurrentSession();
|
||||
ModelObject data = currentSession.getKeyValue("TTD").getKeyValue("Data");
|
||||
ModelMethod heap = data.getMethod("Heap");
|
||||
Pointer[] args = new Pointer[0];
|
||||
ModelObject ret = heap.call(data, 0, args);
|
||||
for (ModelObject heapObj : ret.getElements()) {
|
||||
Map<String, ModelObject> heapMap = heapObj.getKeyValueMap();
|
||||
ModelObject address = heapMap.get("Address");
|
||||
ModelObject size = heapMap.get("Size");
|
||||
ModelObject timeStart = heapMap.get("TimeStart").getKeyValue("Sequence");
|
||||
ModelObject timeEnd = heapMap.get("TimeEnd").getKeyValue("Sequence");
|
||||
if (address == null) {
|
||||
continue;
|
||||
}
|
||||
Long start = (Long) address.getValue();
|
||||
if (size == null) {
|
||||
continue;
|
||||
}
|
||||
Long sz = (Long) size.getValue();
|
||||
if (sz == null) {
|
||||
continue;
|
||||
}
|
||||
AddressRange rng = rng(start, start + sz);
|
||||
String heapId = "Heap " + address.getValueString();
|
||||
Long startTick = (Long) timeStart.getValue();
|
||||
Long stopTick = (Long) timeEnd.getValue();
|
||||
Range<Long> interval =
|
||||
(stopTick > 0) ? Range.open(startTick, stopTick) : Range.atLeast(startTick);
|
||||
addHeap(heapId, interval, rng, TraceMemoryFlag.READ, TraceMemoryFlag.WRITE,
|
||||
TraceMemoryFlag.EXECUTE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Give a program view to Ghidra's program manager
|
||||
*
|
||||
* NOTE: Eventually, there will probably be a TraceManager service as well, but to use the
|
||||
* familiar UI components, we generally take orders from the ProgramManager.
|
||||
*/
|
||||
//manager.openTrace(trace);
|
||||
//manager.activateTrace(trace);
|
||||
List<MemoryBox> boxList = new ArrayList<>();
|
||||
for (MemoryBox memoryBox : boxes.values()) {
|
||||
boxList.add(memoryBox);
|
||||
}
|
||||
Swing.runIfSwingOrRunLater(() -> {
|
||||
memview.setBoxes(boxList);
|
||||
memview.setProgram(currentProgram);
|
||||
memview.initViews();
|
||||
});
|
||||
}
|
||||
|
||||
private void addHeap(String heapId, Range<Long> interval, AddressRange rng,
|
||||
TraceMemoryFlag read, TraceMemoryFlag write, TraceMemoryFlag execute) {
|
||||
MemoryBox box = new MemoryBox(heapId, MemviewBoxType.HEAP_CREATE, rng, interval);
|
||||
boxes.put(box.getId(), box);
|
||||
}
|
||||
|
||||
private void addThread(String threadId, Range<Long> interval, AddressRange rng) {
|
||||
MemoryBox box = new MemoryBox(threadId, MemviewBoxType.THREAD, rng, interval);
|
||||
boxes.put(box.getId(), box);
|
||||
}
|
||||
|
||||
private void addRegion(String regionId, Range<Long> interval, AddressRange rng,
|
||||
TraceMemoryFlag read, TraceMemoryFlag write, TraceMemoryFlag execute) {
|
||||
MemoryBox box = new MemoryBox(regionId, MemviewBoxType.IMAGE, rng, interval);
|
||||
boxes.put(box.getId(), box);
|
||||
}
|
||||
|
||||
private void addLoadedModule(String moduleId, String moduleId2, Range<Long> interval,
|
||||
AddressRange rng) {
|
||||
MemoryBox box = new MemoryBox(moduleId, MemviewBoxType.MODULE, rng, interval);
|
||||
boxes.put(box.getId(), box);
|
||||
}
|
||||
|
||||
private void markThreadClosed(String threadId, Long end) {
|
||||
MemoryBox box = boxes.get(threadId);
|
||||
if (box != null) {
|
||||
if (end < 0) {
|
||||
end = Long.MAX_VALUE;
|
||||
}
|
||||
box.setEnd(end);
|
||||
}
|
||||
}
|
||||
|
||||
private void markModuleClosed(String moduleId, Long end) {
|
||||
MemoryBox box = boxes.get(moduleId);
|
||||
if (box != null) {
|
||||
if (end < 0) {
|
||||
end = Long.MAX_VALUE;
|
||||
}
|
||||
box.setEnd(end);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -42,6 +42,13 @@ src/main/help/help/topics/DebuggerListingPlugin/DebuggerListingPlugin.html||GHID
|
||||
src/main/help/help/topics/DebuggerListingPlugin/images/DebuggerGoToDialog.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/DebuggerListingPlugin/images/DebuggerListingPlugin.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/DebuggerListingPlugin/images/DebuggerModuleImportDialog.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/DebuggerMemviewPlugin/DebuggerMemviewPlugin.html||GHIDRA||||END|
|
||||
src/main/help/help/topics/DebuggerMemviewPlugin/images/DebuggerMemviewPlugin.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/DebuggerMemviewPlugin/images/filter_off.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/DebuggerMemviewPlugin/images/sync_enabled.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/DebuggerMemviewPlugin/images/view-refresh.png||Tango Icons - Public Domain|||tango icon set|END|
|
||||
src/main/help/help/topics/DebuggerMemviewPlugin/images/zoom_in.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END|
|
||||
src/main/help/help/topics/DebuggerMemviewPlugin/images/zoom_out.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END|
|
||||
src/main/help/help/topics/DebuggerModelServicePlugin/DebuggerModelServicePlugin.html||GHIDRA||||END|
|
||||
src/main/help/help/topics/DebuggerModulesPlugin/DebuggerModulesPlugin.html||GHIDRA||||END|
|
||||
src/main/help/help/topics/DebuggerModulesPlugin/images/DebuggerModuleMapProposalDialog.png||GHIDRA||||END|
|
||||
|
@ -132,8 +132,12 @@
|
||||
sortgroup="n"
|
||||
target="help/topics/DebuggerWatchesPlugin/DebuggerWatchesPlugin.html" />
|
||||
|
||||
<tocdef id="DebuggerMemviewPlugin" text="Memview Plot"
|
||||
sortgroup="o"
|
||||
target="help/topics/DebuggerMemviewPlugin/DebuggerMemviewPlugin.html" />
|
||||
|
||||
<tocdef id="DebuggerBots" text="Bots: Workflow Automation"
|
||||
sortgroup="o"
|
||||
sortgroup="p"
|
||||
target="help/topics/DebuggerBots/DebuggerBots.html" />
|
||||
</tocdef>
|
||||
</tocref>
|
||||
|
@ -0,0 +1,84 @@
|
||||
<!DOCTYPE doctype PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN">
|
||||
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<META name="generator" content=
|
||||
"HTML Tidy for Java (vers. 2009-12-01), see jtidy.sourceforge.net">
|
||||
|
||||
<TITLE>Debugger: Memory View</TITLE>
|
||||
<META http-equiv="Content-Type" content="text/html; charset=windows-1252">
|
||||
<LINK rel="stylesheet" type="text/css" href="../../shared/Frontpage.css">
|
||||
</HEAD>
|
||||
|
||||
<BODY lang="EN-US">
|
||||
<H1><A name="plugin"></A>Debugger: Memory View</H1>
|
||||
|
||||
<TABLE width="100%">
|
||||
<TBODY>
|
||||
<TR>
|
||||
<TD align="center" width="100%"><IMG alt="" border="1" src=
|
||||
"images/DebuggerMemviewPlugin.png" style="width:100%"></TD>
|
||||
</TR>
|
||||
</TBODY>
|
||||
</TABLE>
|
||||
|
||||
<P>As in the Threads view, you may wish to follow the evolution of various objects over time.
|
||||
The Debugger Memview Plugin provides a generic time vs. memory (or memory vs. time) plot of
|
||||
objects marked as of interest. The plot may be populated from either a script, typically
|
||||
processing an entire trace, or updated on-the-fly from a live target trace. The objects are
|
||||
listed in the viewer's table on the left for the purpose of quick identification and sorting,
|
||||
and displayed in a scrolled plotting panel on the right.</P>
|
||||
|
||||
<P>The panel does NOT preserve distances. Rather, the objects are plotted based on the order of
|
||||
their lower/upper time/memory bounds. In other words, if two objects are plotted with address
|
||||
ranges [0x100-0x102] and [0x10000000-0x20000000], positions 1-4 on the address axis will
|
||||
correspond to the addresses 0x100, 0x102, 0x10000000, and 0x20000000. Mousing over the object
|
||||
should display it's true bounds, but, in some cases, you will need to locate on the top left
|
||||
corner to get an accurate tool tip. Points past the maximum time or address will not display
|
||||
values for that coordinate.</P>
|
||||
|
||||
<H2>Memview Table</H2>
|
||||
|
||||
<P>The table on the right provides the standard Ghidra table-style interface listing each
|
||||
object, its starting and ending times and starting and ending addresses, if available. Clicking
|
||||
on start/end addresses will navigate in the listing. All columns are sortable, and the tables
|
||||
filterable, as usual. A single selected row will cause a red arrow to be drawn at the top left
|
||||
corner of the corresponding object in the panel. Applying the filter will redisplay only the
|
||||
selected objects in the panel, if that option is enabled.</P>
|
||||
|
||||
<H2>Memview Panel</H2>
|
||||
|
||||
<P>The panel displays all of the object in the table or the table's selection, depending on the
|
||||
options enabled. Clicking on the panel will set the red arrow at that position and display the
|
||||
current position in the title. Doubling-clicking on object will cause it to be centered in the
|
||||
display. The size of the panel can be expanded or contracted on either axis with the Zoom
|
||||
buttons. The top left corner of the panel (which may or may not be visible depending on
|
||||
scrolling) always corresponds to the first address and the first tick. Scroll bars are enabled
|
||||
if any object lies outside the viewable portion of the panel. Drag&drop can also be used to
|
||||
position the panel view. Ctrl-drag&drop draws a box around a region of the display, and the
|
||||
enclosed objects are selected in the table.</P>
|
||||
|
||||
<H2>Navigation</H2>
|
||||
|
||||
<H3><A name="zoom"></A><IMG alt="" src="images/zoom_in.png">Zoom</H3>
|
||||
|
||||
<P>The four zoom buttons either contract or expand the view along the time or address axes.</P>
|
||||
|
||||
<H3><A name="toggle_layout"></A><IMG alt="" src="images/view-refresh.png">Toggle Layout</H3>
|
||||
|
||||
<P>The default panel view is time vs address. The toggle button serves as both the way to
|
||||
switch views and to refresh the display.</P>
|
||||
|
||||
<H3><A name="toggle_process_trace"></A><IMG alt="" src="images/sync_enabled.png">Toggle Process
|
||||
Trace</H3>
|
||||
|
||||
<P>By default, as new objects are added to a debugger trace, they appear in the Memview table
|
||||
and panel. The behavior can be toggled on or off at the user's discretion.</P>
|
||||
|
||||
<H3><A name="apply_to_panel"></A><IMG alt="" src="images/filter_off.png">Toggle Apply
|
||||
Filter</H3>
|
||||
|
||||
<P>This button determines whether the table filter affects the display panel. When toggled on,
|
||||
only selected objects are displayed in the panel.</P>
|
||||
</BODY>
|
||||
</HTML>
|
Binary file not shown.
After Width: | Height: | Size: 190 KiB |
Binary file not shown.
After Width: | Height: | Size: 510 B |
Binary file not shown.
After Width: | Height: | Size: 446 B |
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
Binary file not shown.
After Width: | Height: | Size: 725 B |
Binary file not shown.
After Width: | Height: | Size: 708 B |
@ -0,0 +1,98 @@
|
||||
/* ###
|
||||
* 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.memview;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.plugin.PluginCategoryNames;
|
||||
import ghidra.app.plugin.core.debug.AbstractDebuggerPlugin;
|
||||
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
|
||||
import ghidra.app.plugin.core.debug.event.TraceActivatedPluginEvent;
|
||||
import ghidra.app.services.DebuggerTraceManagerService;
|
||||
import ghidra.framework.plugintool.*;
|
||||
import ghidra.framework.plugintool.util.PluginStatus;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
||||
@PluginInfo( //
|
||||
shortDescription = "Displays memory vs time", //
|
||||
description = "Provides visualiztion/navigation across time/address axes", //
|
||||
category = PluginCategoryNames.DEBUGGER, //
|
||||
packageName = DebuggerPluginPackage.NAME, //
|
||||
status = PluginStatus.RELEASED, //
|
||||
eventsConsumed = { //
|
||||
TraceActivatedPluginEvent.class //
|
||||
}, //
|
||||
servicesRequired = { //
|
||||
DebuggerTraceManagerService.class //
|
||||
}, //
|
||||
servicesProvided = { //
|
||||
MemviewService.class //
|
||||
} //
|
||||
)
|
||||
public class DebuggerMemviewPlugin extends AbstractDebuggerPlugin implements MemviewService {
|
||||
|
||||
protected MemviewProvider provider;
|
||||
private DebuggerMemviewTraceListener listener;
|
||||
|
||||
public DebuggerMemviewPlugin(PluginTool tool) {
|
||||
super(tool);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
provider = new MemviewProvider(getTool(), this);
|
||||
listener = new DebuggerMemviewTraceListener(provider);
|
||||
super.init();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dispose() {
|
||||
tool.removeComponentProvider(provider);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processEvent(PluginEvent event) {
|
||||
super.processEvent(event);
|
||||
if (event instanceof TraceActivatedPluginEvent) {
|
||||
TraceActivatedPluginEvent ev = (TraceActivatedPluginEvent) event;
|
||||
listener.coordinatesActivated(ev.getActiveCoordinates());
|
||||
}
|
||||
}
|
||||
|
||||
public MemviewProvider getProvider() {
|
||||
return provider;
|
||||
}
|
||||
|
||||
public void toggleTrackTrace() {
|
||||
listener.toggleTrackTrace();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoxes(List<MemoryBox> boxList) {
|
||||
provider.setBoxes(boxList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initViews() {
|
||||
provider.initViews();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProgram(Program program) {
|
||||
provider.setProgram(program);
|
||||
}
|
||||
}
|
@ -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.debug.gui.memview;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import ghidra.app.plugin.core.debug.DebuggerCoordinates;
|
||||
import ghidra.app.services.TraceRecorder;
|
||||
import ghidra.async.AsyncDebouncer;
|
||||
import ghidra.async.AsyncTimer;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.trace.model.*;
|
||||
import ghidra.trace.model.Trace.*;
|
||||
import ghidra.trace.model.breakpoint.TraceBreakpoint;
|
||||
import ghidra.trace.model.breakpoint.TraceBreakpointManager;
|
||||
import ghidra.trace.model.memory.TraceMemoryManager;
|
||||
import ghidra.trace.model.memory.TraceMemoryRegion;
|
||||
import ghidra.trace.model.modules.*;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.model.thread.TraceThreadManager;
|
||||
import ghidra.util.Swing;
|
||||
|
||||
public class DebuggerMemviewTraceListener extends TraceDomainObjectListener {
|
||||
|
||||
protected MemviewProvider provider;
|
||||
DebuggerCoordinates current = DebuggerCoordinates.NOWHERE;
|
||||
Trace currentTrace;
|
||||
TraceRecorder currentRecorder;
|
||||
|
||||
private boolean trackTrace = true;
|
||||
private boolean trackThreads = true;
|
||||
private boolean trackRegions = true;
|
||||
private boolean trackModules = true;
|
||||
private boolean trackSections = true;
|
||||
private boolean trackBreakpoints = true;
|
||||
private boolean trackBytes = true;
|
||||
|
||||
private final AsyncDebouncer<Void> updateLabelDebouncer =
|
||||
new AsyncDebouncer<>(AsyncTimer.DEFAULT_TIMER, 100);
|
||||
|
||||
public DebuggerMemviewTraceListener(MemviewProvider provider) {
|
||||
this.provider = provider;
|
||||
|
||||
updateLabelDebouncer.addListener(__ -> Swing.runIfSwingOrRunLater(() -> doUpdateLabel()));
|
||||
|
||||
listenFor(TraceThreadChangeType.ADDED, this::threadChanged);
|
||||
listenFor(TraceThreadChangeType.CHANGED, this::threadChanged);
|
||||
listenFor(TraceThreadChangeType.LIFESPAN_CHANGED, this::threadChanged);
|
||||
listenFor(TraceThreadChangeType.DELETED, this::threadChanged);
|
||||
|
||||
listenFor(TraceMemoryRegionChangeType.ADDED, this::regionChanged);
|
||||
listenFor(TraceMemoryRegionChangeType.CHANGED, this::regionChanged);
|
||||
listenFor(TraceMemoryRegionChangeType.LIFESPAN_CHANGED, this::regionChanged);
|
||||
listenFor(TraceMemoryRegionChangeType.DELETED, this::regionChanged);
|
||||
|
||||
listenFor(TraceModuleChangeType.ADDED, this::moduleChanged);
|
||||
listenFor(TraceModuleChangeType.CHANGED, this::moduleChanged);
|
||||
listenFor(TraceModuleChangeType.LIFESPAN_CHANGED, this::moduleChanged);
|
||||
listenFor(TraceModuleChangeType.DELETED, this::moduleChanged);
|
||||
|
||||
listenFor(TraceSectionChangeType.ADDED, this::sectionChanged);
|
||||
listenFor(TraceSectionChangeType.CHANGED, this::sectionChanged);
|
||||
listenFor(TraceSectionChangeType.DELETED, this::sectionChanged);
|
||||
|
||||
listenFor(TraceBreakpointChangeType.ADDED, this::breakpointChanged);
|
||||
listenFor(TraceBreakpointChangeType.CHANGED, this::breakpointChanged);
|
||||
listenFor(TraceBreakpointChangeType.LIFESPAN_CHANGED, this::breakpointChanged);
|
||||
listenFor(TraceBreakpointChangeType.DELETED, this::breakpointChanged);
|
||||
|
||||
listenFor(TraceMemoryBytesChangeType.CHANGED, this::bytesChanged);
|
||||
}
|
||||
|
||||
public MemviewProvider getProvider() {
|
||||
return provider;
|
||||
}
|
||||
|
||||
protected AddressRange rng(AddressSpace space, long min, long max) {
|
||||
return new AddressRangeImpl(space.getAddress(min), space.getAddress(max));
|
||||
}
|
||||
|
||||
private void threadChanged(TraceThread thread) {
|
||||
if (!trackThreads || !trackTrace) {
|
||||
return;
|
||||
}
|
||||
AddressFactory factory = thread.getTrace().getBaseAddressFactory();
|
||||
AddressSpace defaultSpace = factory.getDefaultAddressSpace();
|
||||
Long threadId = thread.getKey();
|
||||
AddressRange rng = rng(defaultSpace, threadId, threadId);
|
||||
MemoryBox box = new MemoryBox("Thread " + thread.getName(), MemviewBoxType.THREAD, rng,
|
||||
thread.getLifespan());
|
||||
provider.addBox(box);
|
||||
}
|
||||
|
||||
private void regionChanged(TraceMemoryRegion region) {
|
||||
if (!trackRegions || !trackTrace) {
|
||||
return;
|
||||
}
|
||||
MemoryBox box = new MemoryBox("Region " + region.getName(), MemviewBoxType.VIRTUAL_ALLOC,
|
||||
region.getRange(), region.getLifespan());
|
||||
provider.addBox(box);
|
||||
updateLabelDebouncer.contact(null);
|
||||
}
|
||||
|
||||
private void moduleChanged(TraceModule module) {
|
||||
if (!trackModules || !trackTrace) {
|
||||
return;
|
||||
}
|
||||
AddressRange range = module.getRange();
|
||||
AddressRange xrange = new AddressRangeImpl(range.getMinAddress(), range.getMaxAddress());
|
||||
MemoryBox box = new MemoryBox("Module " + module.getName(), MemviewBoxType.MODULE, xrange,
|
||||
module.getLifespan());
|
||||
provider.addBox(box);
|
||||
updateLabelDebouncer.contact(null);
|
||||
}
|
||||
|
||||
private void sectionChanged(TraceSection section) {
|
||||
if (!trackSections || !trackTrace) {
|
||||
return;
|
||||
}
|
||||
MemoryBox box = new MemoryBox("Section " + section.getName(), MemviewBoxType.IMAGE,
|
||||
section.getRange(), section.getModule().getLifespan());
|
||||
provider.addBox(box);
|
||||
updateLabelDebouncer.contact(null);
|
||||
}
|
||||
|
||||
private void breakpointChanged(TraceBreakpoint bpt) {
|
||||
if (!trackBreakpoints || !trackTrace) {
|
||||
return;
|
||||
}
|
||||
MemoryBox box = new MemoryBox("Breakpoint " + bpt.getName(), MemviewBoxType.BREAKPOINT,
|
||||
bpt.getRange(), bpt.getLifespan());
|
||||
provider.addBox(box);
|
||||
updateLabelDebouncer.contact(null);
|
||||
}
|
||||
|
||||
private void bytesChanged(TraceAddressSnapRange range) {
|
||||
if (!trackBytes || !trackTrace) {
|
||||
return;
|
||||
}
|
||||
Range<Long> lifespan = range.getLifespan();
|
||||
Range<Long> newspan = Range.closedOpen(lifespan.lowerEndpoint(), lifespan.lowerEndpoint());
|
||||
MemoryBox box = new MemoryBox("BytesChanged " + range.description(),
|
||||
MemviewBoxType.WRITE_MEMORY, range.getRange(), newspan);
|
||||
provider.addBox(box);
|
||||
updateLabelDebouncer.contact(null);
|
||||
}
|
||||
|
||||
private void doUpdateLabel() {
|
||||
//updateLocationLabel();
|
||||
}
|
||||
|
||||
protected void addListener() {
|
||||
Trace trace = current.getTrace();
|
||||
if (trace != null) {
|
||||
trace.addListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
protected void removeListener() {
|
||||
Trace trace = current.getTrace();
|
||||
if (trace != null) {
|
||||
trace.removeListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void setCoordinates(DebuggerCoordinates coordinates) {
|
||||
if (current.equals(coordinates)) {
|
||||
current = coordinates;
|
||||
return;
|
||||
}
|
||||
boolean doListeners = !Objects.equals(current.getTrace(), coordinates.getTrace());
|
||||
if (doListeners) {
|
||||
removeListener();
|
||||
}
|
||||
current = coordinates;
|
||||
if (doListeners) {
|
||||
addListener();
|
||||
}
|
||||
}
|
||||
|
||||
protected DebuggerCoordinates adjustCoordinates(DebuggerCoordinates coordinates) {
|
||||
// Because the view's snap is changing with or without us.... So go with.
|
||||
return current.withSnap(coordinates.getSnap());
|
||||
}
|
||||
|
||||
public void coordinatesActivated(DebuggerCoordinates coordinates) {
|
||||
//DebuggerCoordinates adjusted = adjustCoordinates(coordinates);
|
||||
setCoordinates(coordinates);
|
||||
Trace trace = coordinates.getTrace();
|
||||
if (trace != null) {
|
||||
Swing.runLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
processTrace(trace);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void traceClosed(Trace trace) {
|
||||
if (current.getTrace() == trace) {
|
||||
setCoordinates(DebuggerCoordinates.NOWHERE);
|
||||
}
|
||||
}
|
||||
|
||||
public void toggleTrackTrace() {
|
||||
trackTrace = !trackTrace;
|
||||
}
|
||||
|
||||
private void processTrace(Trace trace) {
|
||||
provider.reset();
|
||||
TraceThreadManager threadManager = trace.getThreadManager();
|
||||
for (TraceThread thread : threadManager.getAllThreads()) {
|
||||
threadChanged(thread);
|
||||
}
|
||||
TraceModuleManager moduleManager = trace.getModuleManager();
|
||||
for (TraceModule module : moduleManager.getAllModules()) {
|
||||
moduleChanged(module);
|
||||
Collection<? extends TraceSection> sections = module.getSections();
|
||||
for (TraceSection section : sections) {
|
||||
sectionChanged(section);
|
||||
}
|
||||
}
|
||||
TraceMemoryManager memoryManager = trace.getMemoryManager();
|
||||
for (TraceMemoryRegion region : memoryManager.getAllRegions()) {
|
||||
regionChanged(region);
|
||||
}
|
||||
TraceBreakpointManager breakpointManager = trace.getBreakpointManager();
|
||||
for (TraceBreakpoint bpt : breakpointManager.getAllBreakpoints()) {
|
||||
breakpointChanged(bpt);
|
||||
}
|
||||
updateLabelDebouncer.contact(null);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,249 @@
|
||||
/* ###
|
||||
* 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.memview;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
|
||||
public class MemoryBox {
|
||||
|
||||
protected String id;
|
||||
protected MemviewBoxType type;
|
||||
protected AddressRange range;
|
||||
protected long start;
|
||||
protected long stop = Long.MAX_VALUE;
|
||||
protected long startAddr;
|
||||
protected long stopAddr = -1;
|
||||
protected long startTime;
|
||||
protected long stopTime = -1;
|
||||
protected Color color = Color.BLUE;
|
||||
|
||||
protected int pixAstart;
|
||||
protected int pixAend;
|
||||
protected int boundA;
|
||||
protected int pixTstart;
|
||||
protected int pixTend;
|
||||
protected int boundT;
|
||||
|
||||
protected boolean current;
|
||||
|
||||
public MemoryBox(String id, MemviewBoxType type, AddressRange range, long tick) {
|
||||
this.id = id;
|
||||
this.type = type;
|
||||
this.range = range;
|
||||
this.start = tick;
|
||||
this.color = type.getColor();
|
||||
}
|
||||
|
||||
public MemoryBox(String id, MemviewBoxType type, AddressRange range, Range<Long> trange) {
|
||||
this(id, type, range, trange.lowerEndpoint());
|
||||
if (trange.hasUpperBound()) {
|
||||
setEnd(trange.upperEndpoint());
|
||||
}
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public MemviewBoxType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public AddressRange getRange() {
|
||||
return range;
|
||||
}
|
||||
|
||||
public Range<Long> getSpan() {
|
||||
return Range.openClosed(start, stop);
|
||||
}
|
||||
|
||||
public long getStart() {
|
||||
return start;
|
||||
}
|
||||
|
||||
public long getEnd() {
|
||||
return stop;
|
||||
}
|
||||
|
||||
public void setEnd(long tick) {
|
||||
this.stop = stop < tick ? stop : tick;
|
||||
}
|
||||
|
||||
public Color getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
public void setColor(Color color) {
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
public void setColor(Color base, int type) {
|
||||
setColor(new Color(base.getRed(), (base.getGreen() + type) % 255, base.getBlue()));
|
||||
}
|
||||
|
||||
public void setColor(Color base, int type, int src) {
|
||||
setColor(new Color(base.getRed(), (base.getGreen() + type * 8) % 255,
|
||||
(base.getBlue() + src * 16) % 255));
|
||||
}
|
||||
|
||||
public int getAddressPixelStart() {
|
||||
return pixAstart;
|
||||
}
|
||||
|
||||
public int getAddressPixelWidth() {
|
||||
if (pixAend - pixAstart <= 0)
|
||||
return 1;
|
||||
return pixAend - pixAstart;
|
||||
}
|
||||
|
||||
public int getTimePixelStart() {
|
||||
return pixTstart;
|
||||
}
|
||||
|
||||
public int getTimePixelWidth() {
|
||||
if (pixTend < pixTstart) {
|
||||
pixTend = boundT;
|
||||
}
|
||||
if (pixTend - pixTstart == 0)
|
||||
return 1;
|
||||
return pixTend - pixTstart;
|
||||
}
|
||||
|
||||
public int getX(boolean vertical) {
|
||||
return vertical ? pixTstart : pixAstart;
|
||||
}
|
||||
|
||||
public int getY(boolean vertical) {
|
||||
return vertical ? pixAstart : pixTstart;
|
||||
}
|
||||
|
||||
public void setCurrent(boolean current) {
|
||||
this.current = current;
|
||||
}
|
||||
|
||||
public boolean isCurrent() {
|
||||
return current;
|
||||
}
|
||||
|
||||
public void render(Graphics g, boolean vertical) {
|
||||
int x = vertical ? getTimePixelStart() : getAddressPixelStart();
|
||||
int w = vertical ? getTimePixelWidth() : getAddressPixelWidth();
|
||||
int y = vertical ? getAddressPixelStart() : getTimePixelStart();
|
||||
int h = vertical ? getAddressPixelWidth() : getTimePixelWidth();
|
||||
g.setColor(Color.BLACK);
|
||||
g.fillRect(x - 1, y - 1, w + 2, h + 2);
|
||||
g.setColor(color);
|
||||
g.fillRect(x, y, w, h);
|
||||
}
|
||||
|
||||
public void renderBA(Graphics g, boolean vertical, int sz) {
|
||||
int x = vertical ? 0 : getAddressPixelStart();
|
||||
int w = vertical ? sz : getAddressPixelWidth();
|
||||
int y = vertical ? getAddressPixelStart() : 0;
|
||||
int h = vertical ? getAddressPixelWidth() : sz;
|
||||
g.setColor(Color.BLACK);
|
||||
g.fillRect(x - 1, y - 1, w + 2, h + 2);
|
||||
g.setColor(color);
|
||||
g.fillRect(x, y, w, h);
|
||||
}
|
||||
|
||||
public void renderBT(Graphics g, boolean vertical, int sz, int bound) {
|
||||
int x = vertical ? getTimePixelStart() : 0;
|
||||
int w = vertical ? 1 : sz;
|
||||
int y = vertical ? 0 : getTimePixelStart();
|
||||
int h = vertical ? sz : 1;
|
||||
g.setColor(Color.BLACK);
|
||||
g.fillRect(x - 1, y - 1, w + 2, h + 2);
|
||||
g.setColor(color);
|
||||
g.fillRect(x, y, w, h);
|
||||
}
|
||||
|
||||
public void setAddressBounds(MemviewMap map, int bound) {
|
||||
if (stopAddr < 0) {
|
||||
stopAddr = startAddr;
|
||||
}
|
||||
pixAstart = getPixel(map, startAddr, bound);
|
||||
pixAend = getPixel(map, stopAddr, bound);
|
||||
boundA = bound;
|
||||
}
|
||||
|
||||
public void setTimeBounds(MemviewMap map, int bound) {
|
||||
pixTstart = getPixel(map, startTime, bound);
|
||||
pixTend = getPixel(map, stopTime, bound);
|
||||
boundT = bound;
|
||||
}
|
||||
|
||||
protected int getPixel(MemviewMap map, long offset, int bound) {
|
||||
return map.getPixel(offset);
|
||||
}
|
||||
|
||||
public long getStartAddress() {
|
||||
return startAddr;
|
||||
}
|
||||
|
||||
public void setStartAddress(long val) {
|
||||
startAddr = val;
|
||||
}
|
||||
|
||||
public long getStopAddress() {
|
||||
return stopAddr;
|
||||
}
|
||||
|
||||
public void setStopAddress(long val) {
|
||||
stopAddr = val;
|
||||
}
|
||||
|
||||
public long getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
public void setStartTime(long val) {
|
||||
startTime = val;
|
||||
}
|
||||
|
||||
public long getStopTime() {
|
||||
return stopTime;
|
||||
}
|
||||
|
||||
public void setStopTime(long val) {
|
||||
stopTime = val;
|
||||
}
|
||||
|
||||
public boolean inPixelRange(long pos) {
|
||||
if (pos < pixTstart)
|
||||
return false;
|
||||
if (pixTend <= 0)
|
||||
return true;
|
||||
return pos <= pixTend;
|
||||
}
|
||||
|
||||
public Map<String, Object> getAttributeMap() {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("Id", getId());
|
||||
map.put("StartAddr", getStartAddress());
|
||||
map.put("StopAddr", getStopAddress());
|
||||
map.put("StartTime", getStartTime());
|
||||
map.put("StopTIme", getStopTime());
|
||||
return map;
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
/* ###
|
||||
* 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.memview;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
public enum MemviewBoxType {
|
||||
INSTRUCTIONS,
|
||||
PROCESS,
|
||||
THREAD,
|
||||
MODULE,
|
||||
REGION,
|
||||
IMAGE,
|
||||
VIRTUAL_ALLOC,
|
||||
HEAP_CREATE,
|
||||
HEAP_ALLOC,
|
||||
POOL,
|
||||
STACK,
|
||||
PERFINFO,
|
||||
READ_MEMORY,
|
||||
WRITE_MEMORY,
|
||||
BREAKPOINT;
|
||||
|
||||
Color[] colors = { //
|
||||
new Color(128, 000, 000), // INSTRUCTIONS
|
||||
new Color(200, 200, 255), // PROCESS
|
||||
new Color(200, 255, 255), // THREAD
|
||||
Color.GREEN, //new Color(000, 150, 200), // MODULE
|
||||
Color.YELLOW, //new Color(000, 150, 200), // REGION
|
||||
Color.MAGENTA, //new Color(050, 100, 255), // IMAGE
|
||||
Color.LIGHT_GRAY, // VIRTUAL_ALLOC
|
||||
Color.BLUE, // HEAP_CREATE
|
||||
new Color(000, 100, 050), // HEAP_ALLOC
|
||||
new Color(100, 000, 150), // POOL
|
||||
Color.CYAN, // STACK
|
||||
Color.LIGHT_GRAY, // PERFINFO
|
||||
Color.DARK_GRAY, // READ_MEMORY
|
||||
Color.BLUE, // WRITE_MEMORY
|
||||
Color.RED, // WRITE_MEMORY
|
||||
};
|
||||
|
||||
public Color getColor() {
|
||||
return colors[this.ordinal()];
|
||||
}
|
||||
|
||||
public void setColor(Color color) {
|
||||
colors[this.ordinal()] = color;
|
||||
}
|
||||
}
|
@ -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 ghidra.app.plugin.core.debug.gui.memview;
|
||||
|
||||
public class MemviewMap {
|
||||
|
||||
private long max;
|
||||
private long sz;
|
||||
private double elementsPerPixel;
|
||||
private double multiplier;
|
||||
|
||||
public MemviewMap(long elems, long pixels) {
|
||||
max = sz = elems;
|
||||
elementsPerPixel = pixels == 0 ? 0 : elems / pixels;
|
||||
multiplier = 1.0;
|
||||
}
|
||||
|
||||
public void createMapping(double mult) {
|
||||
this.multiplier = mult;
|
||||
}
|
||||
|
||||
public long getOffset(int pixel) {
|
||||
return Math.round(pixel * elementsPerPixel / multiplier);
|
||||
}
|
||||
|
||||
public int getPixel(long offset) {
|
||||
if (offset < 0) {
|
||||
offset = max;
|
||||
}
|
||||
double doffset = offset * multiplier / elementsPerPixel;
|
||||
return (int) Math.round(doffset);
|
||||
}
|
||||
|
||||
public long getSize() {
|
||||
return getPixel(max);
|
||||
}
|
||||
|
||||
public double getMultiplier() {
|
||||
return multiplier;
|
||||
}
|
||||
|
||||
public double getOriginalElemPerPixel() {
|
||||
return elementsPerPixel;
|
||||
}
|
||||
}
|
@ -0,0 +1,245 @@
|
||||
/* ###
|
||||
* 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.memview;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import docking.widgets.table.AbstractSortedTableModel;
|
||||
import ghidra.program.model.address.Address;
|
||||
|
||||
class MemviewMapModel extends AbstractSortedTableModel<MemoryBox> {
|
||||
|
||||
final static byte NAME = 0;
|
||||
final static byte ASTART = 1;
|
||||
final static byte ASTOP = 2;
|
||||
final static byte TSTART = 3;
|
||||
final static byte TSTOP = 4;
|
||||
|
||||
final static String NAME_COL = "Name";
|
||||
final static String ASTART_COL = "Start Address";
|
||||
final static String ASTOP_COL = "End Address";
|
||||
final static String TSTART_COL = "Start Time";
|
||||
final static String TSTOP_COL = "End Time";
|
||||
|
||||
private List<MemoryBox> memList = new ArrayList<>();
|
||||
private Map<String, MemoryBox> memMap = new HashMap<>();
|
||||
private MemviewProvider provider;
|
||||
|
||||
private final static String COLUMN_NAMES[] =
|
||||
{ NAME_COL, ASTART_COL, ASTOP_COL, TSTART_COL, TSTOP_COL };
|
||||
|
||||
public MemviewMapModel(MemviewProvider provider) {
|
||||
super(ASTART);
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
public List<MemoryBox> getBoxes() {
|
||||
return memList;
|
||||
}
|
||||
|
||||
public void addBoxes(Collection<MemoryBox> boxes) {
|
||||
if (memList == null) {
|
||||
memList = new ArrayList<>();
|
||||
}
|
||||
for (MemoryBox b : boxes) {
|
||||
if (memMap.containsKey(b.getId())) {
|
||||
MemoryBox mb = memMap.get(b.getId());
|
||||
memList.remove(mb);
|
||||
}
|
||||
memList.add(b);
|
||||
memMap.put(b.getId(), b);
|
||||
}
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
public void setBoxes(Collection<MemoryBox> boxes) {
|
||||
memList = new ArrayList<>();
|
||||
for (MemoryBox b : boxes) {
|
||||
memList.add(b);
|
||||
memMap.put(b.getId(), b);
|
||||
}
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
memList = new ArrayList<>();
|
||||
memMap.clear();
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
void update() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSortable(int columnIndex) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Memory vs Time Map";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return COLUMN_NAMES.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColumnName(int column) {
|
||||
|
||||
if (column < 0 || column >= COLUMN_NAMES.length) {
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
return COLUMN_NAMES[column];
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method for locating columns by name.
|
||||
* Implementation is naive so this should be overridden if
|
||||
* this method is to be called often. This method is not
|
||||
* in the TableModel interface and is not used by the JTable.
|
||||
*/
|
||||
@Override
|
||||
public int findColumn(String columnName) {
|
||||
for (int i = 0; i < COLUMN_NAMES.length; i++) {
|
||||
if (COLUMN_NAMES[i].equals(columnName)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Object.class by default
|
||||
*/
|
||||
@Override
|
||||
public Class<?> getColumnClass(int columnIndex) {
|
||||
if (columnIndex == ASTART || columnIndex == ASTOP) {
|
||||
return Address.class;
|
||||
}
|
||||
return String.class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this column is editable.
|
||||
*/
|
||||
@Override
|
||||
public boolean isCellEditable(int rowIndex, int columnIndex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of records managed by the data source object. A
|
||||
* <B>JTable</B> uses this method to determine how many rows it
|
||||
* should create and display. This method should be quick, as it
|
||||
* is call by <B>JTable</B> quite frequently.
|
||||
*
|
||||
* @return the number or rows in the model
|
||||
* @see #getColumnCount
|
||||
*/
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
return memList.size();
|
||||
}
|
||||
|
||||
public MemoryBox getBoxAt(int rowIndex) {
|
||||
if (memList == null) {
|
||||
return null;
|
||||
}
|
||||
if (rowIndex < 0 || rowIndex >= memList.size()) {
|
||||
return null;
|
||||
}
|
||||
MemoryBox box = memList.get(rowIndex);
|
||||
try {
|
||||
box.getStart();
|
||||
}
|
||||
catch (ConcurrentModificationException e) {
|
||||
update();
|
||||
}
|
||||
return memList.get(rowIndex);
|
||||
}
|
||||
|
||||
public int getIndexForBox(MemoryBox box) {
|
||||
return memList.indexOf(box);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getColumnValueForRow(MemoryBox box, int columnIndex) {
|
||||
try {
|
||||
switch (columnIndex) {
|
||||
case NAME:
|
||||
return box.getId();
|
||||
case ASTART:
|
||||
return box.getRange().getMinAddress();
|
||||
case ASTOP:
|
||||
return box.getRange().getMaxAddress();
|
||||
case TSTART:
|
||||
return Long.toString(box.getStart());
|
||||
case TSTOP:
|
||||
long end = box.getEnd();
|
||||
if (end == Long.MAX_VALUE) {
|
||||
return "+" + '\u221e' + '\u2025';
|
||||
}
|
||||
return Long.toString(end);
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
catch (ConcurrentModificationException e) {
|
||||
update();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MemoryBox> getModelData() {
|
||||
return memList;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Comparator<MemoryBox> createSortComparator(int columnIndex) {
|
||||
return new MemoryMapComparator(columnIndex);
|
||||
}
|
||||
|
||||
private class MemoryMapComparator implements Comparator<MemoryBox> {
|
||||
private final int sortColumn;
|
||||
|
||||
public MemoryMapComparator(int sortColumn) {
|
||||
this.sortColumn = sortColumn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(MemoryBox b1, MemoryBox b2) {
|
||||
|
||||
switch (sortColumn) {
|
||||
case NAME:
|
||||
return b1.getId().compareToIgnoreCase(b2.getId());
|
||||
case ASTART:
|
||||
return (int) (b1.getStartAddress() - b2.getStartAddress());
|
||||
case ASTOP:
|
||||
return (int) (b1.getStopAddress() - b2.getStopAddress());
|
||||
case TSTART:
|
||||
return (int) (b1.getStartTime() - b2.getStartTime());
|
||||
case TSTOP:
|
||||
return (int) (b1.getStopTime() - b2.getStopTime());
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,489 @@
|
||||
/* ###
|
||||
* 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.memview;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
|
||||
public class MemviewPanel extends JPanel implements MouseListener, MouseMotionListener {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private MemviewProvider provider;
|
||||
private MemviewMap amap;
|
||||
private MemviewMap tmap;
|
||||
private List<MemoryBox> boxList = new ArrayList<MemoryBox>();
|
||||
|
||||
private int pressedX;
|
||||
private int pressedY;
|
||||
private boolean enableDrag = false;
|
||||
private boolean ctrlPressed = false;
|
||||
private int barWidth = 1000;
|
||||
private int barHeight = 500;
|
||||
private boolean vertical = false;
|
||||
|
||||
private int currentPixelAddr = -1;
|
||||
private int currentPixelTime = -1;
|
||||
private Rectangle currentRectangle = null;
|
||||
|
||||
private List<MemoryBox> blist = null;
|
||||
private Map<String, MemoryBox> bmap = new HashMap<>();
|
||||
|
||||
private TreeSet<Address> addresses = new TreeSet<>();
|
||||
private TreeSet<Long> times = new TreeSet<>();
|
||||
private Address[] addressArray;
|
||||
private Long[] timesArray;
|
||||
private Map<Long, Set<MemoryBox>> addr2box = new HashMap<>();
|
||||
private Map<Long, Set<MemoryBox>> time2box = new HashMap<>();
|
||||
|
||||
public MemviewPanel(MemviewProvider provider) {
|
||||
super();
|
||||
this.provider = provider;
|
||||
setPreferredSize(new Dimension(barWidth, barHeight));
|
||||
setSize(getPreferredSize());
|
||||
setBorder(BorderFactory.createLineBorder(Color.BLACK, 1));
|
||||
setFocusable(true);
|
||||
|
||||
addMouseListener(this);
|
||||
addMouseMotionListener(this);
|
||||
ToolTipManager.sharedInstance().registerComponent(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
int asz = amap != null ? (int) (amap.getSize()) : 500;
|
||||
int tsz = tmap != null ? (int) (tmap.getSize()) : 500;
|
||||
int w = vertical ? tsz : asz;
|
||||
int h = vertical ? asz : tsz;
|
||||
return new Dimension(w, h);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintComponent(Graphics g) {
|
||||
super.paintComponent(g);
|
||||
g.setColor(getBackground());
|
||||
Rectangle clip = g.getClipBounds();
|
||||
g.fillRect(clip.x, clip.y, clip.width, clip.height);
|
||||
|
||||
//If the width has changed, force a refresh
|
||||
int height = getHeight();
|
||||
int width = getWidth();
|
||||
if (vertical && clip.height > height || !vertical && clip.width > width) {
|
||||
refresh();
|
||||
return;
|
||||
}
|
||||
|
||||
g.fillRect(0, 0, width, height);
|
||||
|
||||
for (MemoryBox box : boxList) {
|
||||
box.render(g, vertical);
|
||||
}
|
||||
|
||||
//Draw the current location arrow
|
||||
if (currentPixelAddr >= 0) {
|
||||
drawArrow(g);
|
||||
}
|
||||
if (currentRectangle != null) {
|
||||
drawFrame(g);
|
||||
}
|
||||
}
|
||||
|
||||
private static final int LOCATION_BASE_WIDTH = 1;
|
||||
private static final int LOCATION_BASE_HEIGHT = 6;
|
||||
private static final int LOCATION_ARROW_WIDTH = 3;
|
||||
private static final int LOCATION_ARROW_HEIGHT = 9;
|
||||
private static final int[] locXs = { 0, -LOCATION_BASE_WIDTH, -LOCATION_BASE_WIDTH,
|
||||
-LOCATION_ARROW_WIDTH, 0, LOCATION_ARROW_WIDTH, LOCATION_BASE_WIDTH, LOCATION_BASE_WIDTH };
|
||||
private static final int[] locYs = { 0, 0, LOCATION_BASE_HEIGHT, LOCATION_BASE_HEIGHT,
|
||||
LOCATION_ARROW_HEIGHT, LOCATION_BASE_HEIGHT, LOCATION_BASE_HEIGHT, 0 };
|
||||
|
||||
private void drawArrow(Graphics g) {
|
||||
Graphics2D g2 = (Graphics2D) g;
|
||||
if (vertical) {
|
||||
g2.rotate(90.0f / 180.0f * Math.PI);
|
||||
g2.translate(0, LOCATION_ARROW_HEIGHT);
|
||||
g.translate(currentPixelAddr, -currentPixelTime);
|
||||
g2.rotate(Math.PI);
|
||||
}
|
||||
else {
|
||||
g2.translate(0, -LOCATION_ARROW_HEIGHT);
|
||||
g.translate(currentPixelAddr, currentPixelTime);
|
||||
}
|
||||
|
||||
g.setColor(Color.RED);
|
||||
g.fillPolygon(locXs, locYs, locXs.length);
|
||||
|
||||
if (vertical) {
|
||||
g2.rotate(Math.PI);
|
||||
g.translate(-currentPixelAddr, currentPixelTime);
|
||||
g2.translate(0, -LOCATION_ARROW_HEIGHT);
|
||||
g2.rotate(-90.0f / 180.0f * Math.PI);
|
||||
}
|
||||
else {
|
||||
g.translate(-currentPixelAddr, -currentPixelTime);
|
||||
g2.translate(0, LOCATION_ARROW_HEIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
private void drawFrame(Graphics g) {
|
||||
int x = currentRectangle.x;
|
||||
int y = currentRectangle.y;
|
||||
int w = currentRectangle.width;
|
||||
int h = currentRectangle.height;
|
||||
g.setColor(Color.RED);
|
||||
g.fillRect(x - 1, y - 1, 1, h + 2);
|
||||
g.fillRect(x - 1, y - 1, w + 2, 1);
|
||||
g.fillRect(x + w + 1, y - 1, 1, h + 2);
|
||||
g.fillRect(x - 1, y + h + 1, w + 2, 1);
|
||||
}
|
||||
|
||||
void initViews() {
|
||||
setSize(new Dimension(vertical ? times.size() : addresses.size(),
|
||||
vertical ? addresses.size() : times.size()));
|
||||
this.amap = new MemviewMap(addresses.size(), addresses.size());
|
||||
this.tmap = new MemviewMap(times.size(), times.size());
|
||||
}
|
||||
|
||||
public void refresh() {
|
||||
if (amap == null || tmap == null) {
|
||||
return;
|
||||
}
|
||||
if (vertical) {
|
||||
amap.createMapping(provider.getZoomAmountA());
|
||||
tmap.createMapping(provider.getZoomAmountT());
|
||||
}
|
||||
else {
|
||||
amap.createMapping(provider.getZoomAmountA());
|
||||
tmap.createMapping(provider.getZoomAmountT());
|
||||
}
|
||||
|
||||
updateBoxes();
|
||||
}
|
||||
|
||||
void updateBoxes() {
|
||||
if (!this.isShowing())
|
||||
return;
|
||||
|
||||
boxList = new ArrayList<MemoryBox>();
|
||||
Collection<MemoryBox> boxes = getBoxes();
|
||||
if (boxes == null) {
|
||||
return;
|
||||
}
|
||||
for (MemoryBox box : boxes) {
|
||||
if (box == null)
|
||||
continue;
|
||||
|
||||
int bound = vertical ? getHeight() - 1 : getWidth() - 1;
|
||||
box.setAddressBounds(amap, bound);
|
||||
bound = vertical ? getWidth() - 1 : getHeight() - 1;
|
||||
box.setTimeBounds(tmap, bound);
|
||||
|
||||
boxList.add(box);
|
||||
}
|
||||
|
||||
repaint(0, 0, getWidth(), getHeight());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e) {
|
||||
requestFocus();
|
||||
|
||||
ctrlPressed = false;
|
||||
currentRectangle = null;
|
||||
|
||||
if (e.getButton() == MouseEvent.BUTTON1) {
|
||||
enableDrag = true;
|
||||
pressedX = e.getX();
|
||||
pressedY = e.getY();
|
||||
currentPixelAddr = vertical ? pressedY : pressedX;
|
||||
currentPixelTime = vertical ? pressedX : pressedY;
|
||||
provider.selectTableEntry(getBoxesAt(pressedX, pressedY));
|
||||
provider.refresh();
|
||||
}
|
||||
|
||||
if (e.getButton() == MouseEvent.BUTTON2) {
|
||||
System.err.println("BUTTON2");
|
||||
}
|
||||
|
||||
if (e.getButton() == MouseEvent.BUTTON3) {
|
||||
ctrlPressed = true;
|
||||
enableDrag = true;
|
||||
pressedX = e.getX();
|
||||
pressedY = e.getY();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(MouseEvent e) {
|
||||
enableDrag = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
enableDrag = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseEntered(MouseEvent e) {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseExited(MouseEvent e) {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseDragged(MouseEvent e) {
|
||||
if (enableDrag) {
|
||||
if (!ctrlPressed) {
|
||||
provider.goTo(pressedX - e.getX(), pressedY - e.getY());
|
||||
}
|
||||
else {
|
||||
currentRectangle =
|
||||
new Rectangle(pressedX, pressedY, e.getX() - pressedX, e.getY() - pressedY);
|
||||
provider.selectTableEntry(getBoxesIn(currentRectangle));
|
||||
provider.refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseMoved(MouseEvent e) {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
public void setSelection(Set<MemoryBox> boxes) {
|
||||
for (MemoryBox memoryBox : boxes) {
|
||||
currentPixelAddr = memoryBox.pixAstart;
|
||||
currentPixelTime = memoryBox.pixTstart;
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
public String getTitleAnnotation() {
|
||||
if (currentPixelAddr < 0 || addressArray == null) {
|
||||
return "";
|
||||
}
|
||||
String aval = getTagForAddr(currentPixelAddr);
|
||||
String tval = getTagForTick(currentPixelTime);
|
||||
String vals = vertical ? tval + ":" + aval : aval + ":" + tval;
|
||||
return "curpos=[" + vals + "]";
|
||||
}
|
||||
|
||||
public Set<MemoryBox> getBoxesAt(int x, int y) {
|
||||
long addr = getAddr(x, y);
|
||||
long tick = getTick(x, y);
|
||||
long pos = vertical ? x : y;
|
||||
Set<MemoryBox> matches = new HashSet<>();
|
||||
Set<MemoryBox> mboxes = addr2box.get(addr);
|
||||
if (mboxes != null && tick < timesArray.length) {
|
||||
for (MemoryBox memoryBox : mboxes) {
|
||||
if (memoryBox.inPixelRange(pos)) {
|
||||
matches.add(memoryBox);
|
||||
}
|
||||
}
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
|
||||
public Set<MemoryBox> getBoxesIn(Rectangle r) {
|
||||
long startAddr = getAddr(r.x, r.y);
|
||||
long startTick = getTick(r.x, r.y);
|
||||
long stopAddr = getAddr(r.x + r.width, r.y + r.height);
|
||||
long stopTick = getTick(r.x + r.width, r.y + r.height);
|
||||
Set<MemoryBox> matches = new HashSet<>();
|
||||
for (long addr = startAddr; addr < stopAddr; addr++) {
|
||||
Set<MemoryBox> mboxes = addr2box.get(addr);
|
||||
for (MemoryBox memoryBox : mboxes) {
|
||||
if (memoryBox.getStartTime() >= startTick || memoryBox.getStopTime() <= stopTick) {
|
||||
matches.add(memoryBox);
|
||||
}
|
||||
}
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getToolTipText(MouseEvent e) {
|
||||
if (amap == null || tmap == null) {
|
||||
return e.getX() + ":" + e.getY();
|
||||
}
|
||||
long addr = getAddr(e.getX(), e.getY());
|
||||
long tick = getTick(e.getX(), e.getY());
|
||||
String aval = getTagForAddr(addr);
|
||||
String tval = getTagForTick(tick);
|
||||
Set<MemoryBox> boxes = getBoxesAt(e.getX(), e.getY());
|
||||
for (MemoryBox memoryBox : boxes) {
|
||||
aval = memoryBox.getId();
|
||||
}
|
||||
return vertical ? tval + ":" + aval : aval + ":" + tval;
|
||||
}
|
||||
|
||||
private void parseBoxes(Collection<MemoryBox> boxes) {
|
||||
addresses.clear();
|
||||
times.clear();
|
||||
addr2box.clear();
|
||||
time2box.clear();
|
||||
|
||||
for (MemoryBox box : boxes) {
|
||||
AddressRange range = box.getRange();
|
||||
if (range != null) {
|
||||
addresses.add(range.getMinAddress());
|
||||
addresses.add(range.getMaxAddress());
|
||||
}
|
||||
long start = box.getStart();
|
||||
long end = box.getEnd();
|
||||
times.add(start);
|
||||
times.add(end);
|
||||
}
|
||||
|
||||
initViews();
|
||||
addressArray = new Address[addresses.size()];
|
||||
timesArray = new Long[times.size()];
|
||||
addresses.toArray(addressArray);
|
||||
times.toArray(timesArray);
|
||||
|
||||
for (MemoryBox box : boxes) {
|
||||
AddressRange range = box.getRange();
|
||||
if (range != null) {
|
||||
box.setStartAddress(addresses.headSet(range.getMinAddress()).size());
|
||||
box.setStopAddress(addresses.headSet(range.getMaxAddress()).size());
|
||||
}
|
||||
box.setStartTime(times.headSet(box.getStart()).size());
|
||||
box.setStopTime(times.headSet(box.getEnd()).size());
|
||||
|
||||
Set<MemoryBox> mboxes = addr2box.get(box.getStartAddress());
|
||||
if (mboxes == null) {
|
||||
mboxes = new HashSet<MemoryBox>();
|
||||
}
|
||||
mboxes.add(box);
|
||||
addr2box.put(box.getStartAddress(), mboxes);
|
||||
mboxes = addr2box.get(box.getStopAddress());
|
||||
if (mboxes == null) {
|
||||
mboxes = new HashSet<MemoryBox>();
|
||||
}
|
||||
mboxes.add(box);
|
||||
addr2box.put(box.getStopAddress(), mboxes);
|
||||
|
||||
mboxes = time2box.get(box.getStartTime());
|
||||
if (mboxes == null) {
|
||||
mboxes = new HashSet<MemoryBox>();
|
||||
}
|
||||
mboxes.add(box);
|
||||
time2box.put(box.getStartTime(), mboxes);
|
||||
mboxes = time2box.get(box.getStopTime());
|
||||
if (mboxes == null) {
|
||||
mboxes = new HashSet<MemoryBox>();
|
||||
}
|
||||
mboxes.add(box);
|
||||
time2box.put(box.getStopTime(), mboxes);
|
||||
}
|
||||
refresh();
|
||||
}
|
||||
|
||||
public List<MemoryBox> getBoxes() {
|
||||
return blist;
|
||||
}
|
||||
|
||||
public void setBoxes(List<MemoryBox> boxes) {
|
||||
this.blist = boxes;
|
||||
for (MemoryBox b : boxes) {
|
||||
bmap.put(b.getId(), b);
|
||||
}
|
||||
parseBoxes(blist);
|
||||
}
|
||||
|
||||
public void addBoxes(List<MemoryBox> boxes) {
|
||||
if (blist == null) {
|
||||
blist = new ArrayList<MemoryBox>();
|
||||
}
|
||||
for (MemoryBox b : boxes) {
|
||||
if (bmap.containsKey(b.getId())) {
|
||||
MemoryBox box = bmap.get(b.getId());
|
||||
blist.remove(box);
|
||||
}
|
||||
blist.add(b);
|
||||
bmap.put(b.getId(), b);
|
||||
}
|
||||
parseBoxes(blist);
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
blist = new ArrayList<MemoryBox>();
|
||||
bmap.clear();
|
||||
parseBoxes(blist);
|
||||
}
|
||||
|
||||
void setAddressPixelMap(MemviewMap map) {
|
||||
this.amap = map;
|
||||
}
|
||||
|
||||
void setTimePixelMap(MemviewMap tmap) {
|
||||
this.tmap = tmap;
|
||||
}
|
||||
|
||||
public boolean getVerticalMode() {
|
||||
return vertical;
|
||||
}
|
||||
|
||||
public void setVerticalMode(boolean vertical) {
|
||||
this.vertical = vertical;
|
||||
}
|
||||
|
||||
public long getAddr(int x, int y) {
|
||||
if (amap == null)
|
||||
return 0;
|
||||
return vertical ? amap.getOffset(y) : amap.getOffset(x);
|
||||
}
|
||||
|
||||
public long getTick(int x, int y) {
|
||||
if (tmap == null)
|
||||
return 0;
|
||||
return vertical ? tmap.getOffset(x) : tmap.getOffset(y);
|
||||
}
|
||||
|
||||
public String getTagForAddr(long addr) {
|
||||
String aval = "";
|
||||
if (0 <= addr && addr < addressArray.length) {
|
||||
aval = addressArray[(int) addr].toString();
|
||||
}
|
||||
return aval;
|
||||
}
|
||||
|
||||
public String getTagForTick(long tick) {
|
||||
String tval = "";
|
||||
if (0 <= tick && tick < timesArray.length) {
|
||||
tval = Long.toString(timesArray[(int) tick]);
|
||||
}
|
||||
return tval;
|
||||
}
|
||||
|
||||
public void scaleCurrentPixelAddr(double changeAmount) {
|
||||
this.currentPixelAddr = (int) (currentPixelAddr * Math.pow(2.0, changeAmount));
|
||||
}
|
||||
|
||||
public void scaleCurrentPixelTime(double changeAmount) {
|
||||
this.currentPixelTime = (int) (currentPixelTime * Math.pow(2.0, changeAmount));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,302 @@
|
||||
/* ###
|
||||
* 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.memview;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.DockingAction;
|
||||
import docking.action.builder.ToggleActionBuilder;
|
||||
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
|
||||
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
|
||||
import ghidra.app.plugin.core.debug.gui.DebuggerResources.AbstractRefreshAction;
|
||||
import ghidra.app.plugin.core.debug.gui.memview.actions.*;
|
||||
import ghidra.app.services.DebuggerListingService;
|
||||
import ghidra.framework.plugintool.*;
|
||||
import ghidra.framework.plugintool.AutoService.Wiring;
|
||||
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.Swing;
|
||||
|
||||
public class MemviewProvider extends ComponentProviderAdapter {
|
||||
|
||||
private static final String TITLE = "Memview";
|
||||
|
||||
private Wiring autoServiceWiring;
|
||||
@AutoServiceConsumed
|
||||
private DebuggerListingService listingService;
|
||||
|
||||
private DebuggerMemviewPlugin plugin;
|
||||
private JComponent mainPanel;
|
||||
private MemviewPanel memviewPanel;
|
||||
private MemviewTable memviewTable;
|
||||
private JScrollPane scrollPane;
|
||||
|
||||
//private Address location;
|
||||
private boolean vertical = true;
|
||||
private boolean applyFilter = true;
|
||||
|
||||
private double zoomAmountA = 1.0;
|
||||
private double zoomAmountT = 1.0;
|
||||
long halfPage = 512L;
|
||||
|
||||
public MemviewProvider(PluginTool tool, DebuggerMemviewPlugin plugin) {
|
||||
super(tool, TITLE, plugin.getName());
|
||||
this.plugin = plugin;
|
||||
|
||||
this.autoServiceWiring = AutoService.wireServicesConsumed(plugin, this);
|
||||
|
||||
//setIcon(DebuggerResources.ICON_PROVIDER_REGIONS);
|
||||
//setHelpLocation(DebuggerResources.HELP_PROVIDER_REGIONS);
|
||||
setWindowMenuGroup(DebuggerPluginPackage.NAME);
|
||||
|
||||
mainPanel = new JPanel(new BorderLayout());
|
||||
mainPanel.addComponentListener(new ComponentAdapter() {
|
||||
@Override
|
||||
public void componentResized(ComponentEvent e) {
|
||||
resized();
|
||||
}
|
||||
});
|
||||
tool.addComponentProvider(this, false);
|
||||
createActions();
|
||||
buildPanel();
|
||||
}
|
||||
|
||||
private void createActions() {
|
||||
|
||||
DockingAction zoomInAAction = new ZoomInAAction(this);
|
||||
tool.addLocalAction(this, zoomInAAction);
|
||||
|
||||
DockingAction zoomOutAAction = new ZoomOutAAction(this);
|
||||
tool.addLocalAction(this, zoomOutAAction);
|
||||
|
||||
DockingAction zoomInTAction = new ZoomInTAction(this);
|
||||
tool.addLocalAction(this, zoomInTAction);
|
||||
|
||||
DockingAction zoomOutTAction = new ZoomOutTAction(this);
|
||||
tool.addLocalAction(this, zoomOutTAction);
|
||||
|
||||
new ToggleActionBuilder("Toggle Layout", plugin.getName()) //
|
||||
//.menuPath("&Toggle layout") //
|
||||
.toolBarIcon(AbstractRefreshAction.ICON)
|
||||
.helpLocation(new HelpLocation(plugin.getName(), "toggle_layout")) //
|
||||
.onAction(ctx -> performToggleLayout(ctx))
|
||||
.buildAndInstallLocal(this);
|
||||
|
||||
new ToggleActionBuilder("Toggle Process Trace", plugin.getName()) //
|
||||
//.menuPath("&Toggle layout") //
|
||||
.toolBarIcon(DebuggerResources.ICON_SYNC)
|
||||
.helpLocation(new HelpLocation(plugin.getName(), "toggle_process_trace")) //
|
||||
.onAction(ctx -> performToggleTrace(ctx))
|
||||
.selected(true)
|
||||
.buildAndInstallLocal(this);
|
||||
|
||||
new ToggleActionBuilder("Apply Filter To Panel", plugin.getName()) //
|
||||
//.menuPath("&Toggle layout") //
|
||||
.toolBarIcon(DebuggerResources.ICON_FILTER)
|
||||
.helpLocation(new HelpLocation(plugin.getName(), "apply_to_panel")) //
|
||||
.onAction(ctx -> performApplyFilterToPanel(ctx))
|
||||
.selected(true)
|
||||
.buildAndInstallLocal(this);
|
||||
|
||||
}
|
||||
|
||||
void buildPanel() {
|
||||
mainPanel.removeAll();
|
||||
memviewPanel = new MemviewPanel(this);
|
||||
memviewTable = new MemviewTable(this);
|
||||
|
||||
scrollPane = new JScrollPane(memviewPanel);
|
||||
scrollPane.setPreferredSize(memviewPanel.getSize());
|
||||
|
||||
JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
|
||||
splitPane.setRightComponent(scrollPane);
|
||||
splitPane.setLeftComponent(memviewTable.getComponent());
|
||||
splitPane.setDividerLocation(0.5);
|
||||
mainPanel.add(splitPane, BorderLayout.CENTER);
|
||||
//mainPanel.add(memviewTable.getComponent(), BorderLayout.WEST);
|
||||
|
||||
setDirection();
|
||||
|
||||
mainPanel.validate();
|
||||
}
|
||||
|
||||
private void performToggleLayout(ActionContext ctx) {
|
||||
vertical = !vertical;
|
||||
setDirection();
|
||||
refresh();
|
||||
}
|
||||
|
||||
private void performToggleTrace(ActionContext ctx) {
|
||||
plugin.toggleTrackTrace();
|
||||
}
|
||||
|
||||
private void performApplyFilterToPanel(ActionContext ctx) {
|
||||
applyFilter = !isApplyFilter();
|
||||
if (applyFilter) {
|
||||
memviewTable.applyFilter();
|
||||
}
|
||||
else {
|
||||
memviewPanel.setBoxes(memviewTable.getBoxes());
|
||||
}
|
||||
refresh();
|
||||
}
|
||||
|
||||
private void setDirection() {
|
||||
memviewPanel.setVerticalMode(vertical);
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
tool.removeComponentProvider(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionContext getActionContext(MouseEvent event) {
|
||||
if (event != null && event.getSource() == mainPanel) {
|
||||
return new ActionContext(this, mainPanel);
|
||||
}
|
||||
if (event != null && event.getSource() == memviewPanel) {
|
||||
return new ActionContext(this, memviewPanel);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getComponent() {
|
||||
return mainPanel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HelpLocation getHelpLocation() {
|
||||
return new HelpLocation(plugin.getName(), plugin.getName());
|
||||
}
|
||||
|
||||
public void setProgram(Program program) {
|
||||
memviewTable.setProgram(program);
|
||||
}
|
||||
|
||||
public void initViews() {
|
||||
memviewPanel.initViews();
|
||||
memviewPanel.setPreferredSize(new Dimension(300, 100));
|
||||
memviewTable.setListingService(listingService);
|
||||
mainPanel.doLayout();
|
||||
mainPanel.repaint();
|
||||
}
|
||||
|
||||
public void refresh() {
|
||||
String subTitle = " (" + zoomAmountA + "x:" + zoomAmountT + ") ";
|
||||
subTitle += memviewPanel.getTitleAnnotation();
|
||||
setSubTitle(subTitle);
|
||||
memviewPanel.refresh();
|
||||
scrollPane.getViewport().doLayout();
|
||||
}
|
||||
|
||||
public void goTo(int x, int y) {
|
||||
Rectangle bounds = scrollPane.getBounds();
|
||||
scrollPane.getViewport()
|
||||
.scrollRectToVisible(new Rectangle(x, y, bounds.width, bounds.height));
|
||||
scrollPane.getViewport().doLayout();
|
||||
}
|
||||
|
||||
public void goTo(MemoryBox box) {
|
||||
Point p = new Point(box.getX(vertical) - 10, box.getY(vertical) - 10);
|
||||
Point p0 = scrollPane.getViewport().getViewPosition();
|
||||
int w = scrollPane.getViewport().getWidth();
|
||||
int h = scrollPane.getViewport().getHeight();
|
||||
if (p.x > p0.x && p.x < p0.x + w && p.y > p0.y && p.y < p0.y + h) {
|
||||
return;
|
||||
}
|
||||
scrollPane.getViewport().setViewPosition(p);
|
||||
}
|
||||
|
||||
public void selectTableEntry(Set<MemoryBox> boxes) {
|
||||
memviewTable.setSelection(boxes);
|
||||
}
|
||||
|
||||
public void selectPanelPosition(Set<MemoryBox> boxes) {
|
||||
memviewPanel.setSelection(boxes);
|
||||
if (boxes.size() == 1) {
|
||||
Iterator<MemoryBox> iterator = boxes.iterator();
|
||||
goTo(iterator.next());
|
||||
}
|
||||
}
|
||||
|
||||
public void changeZoomA(int changeAmount) {
|
||||
this.zoomAmountA = (float) (zoomAmountA * Math.pow(2.0, changeAmount));
|
||||
memviewPanel.scaleCurrentPixelAddr(changeAmount);
|
||||
}
|
||||
|
||||
public double getZoomAmountA() {
|
||||
return zoomAmountA;
|
||||
}
|
||||
|
||||
public void changeZoomT(int changeAmount) {
|
||||
this.zoomAmountT = (float) (zoomAmountT * Math.pow(2.0, changeAmount));
|
||||
memviewPanel.scaleCurrentPixelTime(changeAmount);
|
||||
}
|
||||
|
||||
public double getZoomAmountT() {
|
||||
return zoomAmountT;
|
||||
}
|
||||
|
||||
void resized() {
|
||||
memviewPanel.refresh();
|
||||
}
|
||||
|
||||
public void setBoxes(List<MemoryBox> blist) {
|
||||
Swing.runIfSwingOrRunLater(() -> {
|
||||
memviewTable.setBoxes(blist);
|
||||
memviewPanel.setBoxes(blist);
|
||||
});
|
||||
}
|
||||
|
||||
public void setBoxesInPanel(List<MemoryBox> blist) {
|
||||
Swing.runIfSwingOrRunLater(() -> {
|
||||
memviewPanel.setBoxes(blist);
|
||||
memviewPanel.refresh();
|
||||
});
|
||||
}
|
||||
|
||||
public void addBox(MemoryBox box) {
|
||||
List<MemoryBox> blist = new ArrayList<>();
|
||||
blist.add(box);
|
||||
addBoxes(blist);
|
||||
}
|
||||
|
||||
public void addBoxes(List<MemoryBox> blist) {
|
||||
Swing.runIfSwingOrRunLater(() -> {
|
||||
memviewTable.addBoxes(blist);
|
||||
memviewPanel.addBoxes(blist);
|
||||
});
|
||||
}
|
||||
|
||||
public boolean isApplyFilter() {
|
||||
return applyFilter;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
Swing.runIfSwingOrRunLater(() -> {
|
||||
memviewTable.reset();
|
||||
memviewPanel.reset();
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/* ###
|
||||
* 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.memview;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.framework.plugintool.ServiceInfo;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
||||
/**
|
||||
* The MemviewService provides a general service for displaying objects
|
||||
* on time vs. memory axes (a la Boxes)
|
||||
*/
|
||||
@ServiceInfo(defaultProvider = DebuggerMemviewPlugin.class, description = "Display memory vs. time events")
|
||||
public interface MemviewService {
|
||||
|
||||
public void setBoxes(List<MemoryBox> boxList);
|
||||
|
||||
public void initViews();
|
||||
|
||||
public void setProgram(Program currentProgram);
|
||||
|
||||
public MemviewProvider getProvider();
|
||||
|
||||
}
|
@ -0,0 +1,288 @@
|
||||
/* ###
|
||||
* 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.memview;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
|
||||
import docking.widgets.filter.FilterListener;
|
||||
import ghidra.app.services.DebuggerListingService;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressRangeImpl;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.table.GhidraTable;
|
||||
import ghidra.util.table.GhidraTableFilterPanel;
|
||||
import ghidra.util.task.SwingUpdateManager;
|
||||
import resources.ResourceManager;
|
||||
|
||||
public class MemviewTable {
|
||||
|
||||
public static final ImageIcon ICON_TABLE = ResourceManager.loadImage("images/table.png");
|
||||
|
||||
private MemviewMapModel model;
|
||||
private GhidraTable table;
|
||||
private GhidraTableFilterPanel<?> filterPanel;
|
||||
private FilterListener filterListener;
|
||||
private SwingUpdateManager applyFilterManager = new SwingUpdateManager(this::applyFilter);
|
||||
private JPanel component;
|
||||
|
||||
private MemviewProvider provider;
|
||||
private Program program;
|
||||
private DebuggerListingService listingService;
|
||||
|
||||
public MemviewTable(MemviewProvider provider) {
|
||||
this.provider = provider;
|
||||
this.model = new MemviewMapModel(provider);
|
||||
this.table = new GhidraTable(model);
|
||||
table.setHTMLRenderingEnabled(true);
|
||||
this.component = new JPanel(new BorderLayout());
|
||||
JScrollPane scrollPane = new JScrollPane(table);
|
||||
filterPanel = new GhidraTableFilterPanel<>(table, model);
|
||||
component.add(scrollPane, BorderLayout.CENTER);
|
||||
component.add(filterPanel, BorderLayout.SOUTH);
|
||||
table.setAutoscrolls(true);
|
||||
|
||||
table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
|
||||
@Override
|
||||
public void valueChanged(ListSelectionEvent e) {
|
||||
if (e.getValueIsAdjusting()) {
|
||||
return;
|
||||
}
|
||||
int modelRow = filterPanel.getModelRow(table.getSelectedRow());
|
||||
MemoryBox box = model.getBoxAt(modelRow);
|
||||
if (box != null) {
|
||||
Set<MemoryBox> boxes = new HashSet<MemoryBox>();
|
||||
boxes.add(box);
|
||||
provider.selectPanelPosition(boxes);
|
||||
}
|
||||
}
|
||||
});
|
||||
table.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
if (e.getClickCount() == 2) {
|
||||
navigateToSelectedObject();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
filterListener = new FilterActionFilterListener();
|
||||
filterPanel.addFilterChagnedListener(filterListener);
|
||||
}
|
||||
|
||||
public JComponent getComponent() {
|
||||
return component;
|
||||
}
|
||||
|
||||
public JComponent getPrincipalComponent() {
|
||||
return table;
|
||||
}
|
||||
|
||||
public void setProgram(Program program) {
|
||||
this.program = program;
|
||||
}
|
||||
|
||||
public void setListingService(DebuggerListingService listingService) {
|
||||
this.listingService = listingService;
|
||||
}
|
||||
|
||||
public void setBoxes(Collection<MemoryBox> blist) {
|
||||
model.setBoxes(blist);
|
||||
}
|
||||
|
||||
public void addBoxes(Collection<MemoryBox> blist) {
|
||||
model.addBoxes(blist);
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
model.reset();
|
||||
}
|
||||
|
||||
public List<MemoryBox> getBoxes() {
|
||||
return model.getBoxes();
|
||||
}
|
||||
|
||||
public void setSelection(Set<MemoryBox> set) {
|
||||
table.clearSelection();
|
||||
for (MemoryBox box : set) {
|
||||
int index = model.getIndexForBox(box);
|
||||
int viewRow = filterPanel.getViewRow(index);
|
||||
if (viewRow >= 0) {
|
||||
table.addRowSelectionInterval(viewRow, viewRow);
|
||||
table.scrollToSelectedRow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void navigateToSelectedObject() {
|
||||
int selectedRow = table.getSelectedRow();
|
||||
int selectedColumn = table.getSelectedColumn();
|
||||
Object value = table.getValueAt(selectedRow, selectedColumn);
|
||||
Address addr = null;
|
||||
if (value instanceof Address) {
|
||||
addr = (Address) value;
|
||||
}
|
||||
if (value instanceof AddressRangeImpl) {
|
||||
AddressRangeImpl range = (AddressRangeImpl) value;
|
||||
addr = range.getMinAddress();
|
||||
}
|
||||
if (value instanceof Long) {
|
||||
Long lval = (Long) value;
|
||||
if (program != null) {
|
||||
addr = program.getAddressFactory().getAddressSpace("ram").getAddress(lval);
|
||||
}
|
||||
}
|
||||
if (listingService != null) {
|
||||
listingService.goTo(addr, true);
|
||||
}
|
||||
}
|
||||
|
||||
public void applyFilter() {
|
||||
List<MemoryBox> blist = new ArrayList<>();
|
||||
for (int i = 0; i < filterPanel.getRowCount(); i++) {
|
||||
int row = filterPanel.getModelRow(i);
|
||||
if (row >= 0) {
|
||||
blist.add(model.getBoxAt(row));
|
||||
}
|
||||
}
|
||||
provider.setBoxesInPanel(blist);
|
||||
}
|
||||
|
||||
private class FilterActionFilterListener implements FilterListener {
|
||||
@Override
|
||||
public void filterChanged(String text) {
|
||||
if (provider.isApplyFilter()) {
|
||||
applyFilterManager.updateLater();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
private List<MemviewRow> generateRows(Collection<MemoryBox> changed) {
|
||||
List<MemviewRow> list = new ArrayList<>();
|
||||
for (MemoryBox box : changed) {
|
||||
list.add(new MemviewRow(box));
|
||||
}
|
||||
if (model instanceof EnumeratedColumnTableModel) {
|
||||
@SuppressWarnings("unchecked")
|
||||
EnumeratedColumnTableModel<MemviewRow> m =
|
||||
(EnumeratedColumnTableModel<MemviewRow>) model;
|
||||
m.clear();
|
||||
m.addAll(list);
|
||||
}
|
||||
setColumns();
|
||||
model.fireTableStructureChanged();
|
||||
return list;
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
private MemviewRow findMatch(MemoryBox changed) {
|
||||
MemviewRow match = null;
|
||||
for (int i = 0; i < model.getRowCount(); i++) {
|
||||
MemviewRow row = model.getRowObject(i);
|
||||
if (row.getBox().equals(changed)) {
|
||||
row.setAttributes(changed.getAttributeMap());
|
||||
match = row;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return match;
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
private List<MemviewRow> updateMatch(MemviewRow match) {
|
||||
MemviewEnumeratedColumnTableModel m = (MemviewEnumeratedColumnTableModel) model;
|
||||
m.updateColumns(match);
|
||||
m.fireTableDataChanged();
|
||||
List<MemviewRow> list = new ArrayList<>();
|
||||
if (match != null) {
|
||||
list.add(match);
|
||||
model.setLastSelectedObjects(list);
|
||||
model.fireTableStructureChanged();
|
||||
}
|
||||
return list;
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
public void setColumns() {
|
||||
MemviewEnumeratedColumnTableModel m = (MemviewEnumeratedColumnTableModel) model;
|
||||
for (int i = 0; i < model.getRowCount(); i++) {
|
||||
MemviewRow r = model.getRowObject(i);
|
||||
m.updateColumns(r);
|
||||
}
|
||||
m.fireTableStructureChanged();
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
public TargetObject getSelectedObject() {
|
||||
int selectedColumn = table.getSelectedColumn();
|
||||
R r = model.getRowObject(table.getSelectedRow());
|
||||
if (r instanceof ObjectAttributeRow) {
|
||||
ObjectAttributeRow row = (ObjectAttributeRow) r;
|
||||
return row.getTargetObject();
|
||||
}
|
||||
if (r instanceof MemviewRow) {
|
||||
MemviewRow row = (MemviewRow) r;
|
||||
TargetObject targetObject = row.getTargetObject();
|
||||
if (selectedColumn > 0) {
|
||||
List<String> keys = row.getKeys();
|
||||
if (selectedColumn >= keys.size()) {
|
||||
selectedColumn = 0;
|
||||
}
|
||||
String key = keys.get(selectedColumn);
|
||||
Map<String, ?> attributes = targetObject.getCachedAttributes();
|
||||
Object object = attributes.get(key);
|
||||
if (object instanceof TargetObject) {
|
||||
return (TargetObject) object;
|
||||
}
|
||||
}
|
||||
return targetObject;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
public void setSelectedObject(MemoryBox selection) {
|
||||
for (int i = 0; i < model.getRowCount(); i++) {
|
||||
MemviewRow row = model.getRowObject(i);
|
||||
if (row.getBox().equals(selection)) {
|
||||
table.selectRow(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
public void setFocus(MemoryBox focused) {
|
||||
Swing.runIfSwingOrRunLater(() -> {
|
||||
setSelectedObject(focused);
|
||||
});
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/* ###
|
||||
* 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.memview.actions;
|
||||
|
||||
import javax.swing.ImageIcon;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.DockingAction;
|
||||
import docking.action.ToolBarData;
|
||||
import ghidra.app.plugin.core.debug.gui.memview.MemviewProvider;
|
||||
import ghidra.util.HelpLocation;
|
||||
import resources.ResourceManager;
|
||||
|
||||
public class ZoomInAAction extends DockingAction {
|
||||
|
||||
private final ImageIcon ICON = ResourceManager.loadImage("images/zoom_in.png");
|
||||
|
||||
private MemviewProvider provider;
|
||||
|
||||
public ZoomInAAction(MemviewProvider provider) {
|
||||
super("Zoom In (Addrs)", provider.getName());
|
||||
this.provider = provider;
|
||||
setEnabled(true);
|
||||
|
||||
this.setToolBarData(new ToolBarData(ICON, "aoverview"));
|
||||
|
||||
setDescription("Zoom In (A)");
|
||||
setHelpLocation(new HelpLocation("DebuggerMemviewPlugin", "zoom"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
provider.changeZoomA(1);
|
||||
provider.refresh();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/* ###
|
||||
* 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.memview.actions;
|
||||
|
||||
import javax.swing.ImageIcon;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.DockingAction;
|
||||
import docking.action.ToolBarData;
|
||||
import ghidra.app.plugin.core.debug.gui.memview.MemviewProvider;
|
||||
import ghidra.util.HelpLocation;
|
||||
import resources.ResourceManager;
|
||||
|
||||
public class ZoomInTAction extends DockingAction {
|
||||
|
||||
private final ImageIcon ICON = ResourceManager.loadImage("images/zoom_in.png");
|
||||
|
||||
private MemviewProvider provider;
|
||||
|
||||
public ZoomInTAction(MemviewProvider provider) {
|
||||
super("Zoom In (Time)", provider.getName());
|
||||
this.provider = provider;
|
||||
setEnabled(true);
|
||||
|
||||
this.setToolBarData(new ToolBarData(ICON, "aoverview"));
|
||||
|
||||
setDescription("Zoom In (T)");
|
||||
setHelpLocation(new HelpLocation("DebuggerMemviewPlugin", "zoom"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
provider.changeZoomT(1);
|
||||
provider.refresh();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/* ###
|
||||
* 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.memview.actions;
|
||||
|
||||
import javax.swing.ImageIcon;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.DockingAction;
|
||||
import docking.action.ToolBarData;
|
||||
import ghidra.app.plugin.core.debug.gui.memview.MemviewProvider;
|
||||
import ghidra.util.HelpLocation;
|
||||
import resources.ResourceManager;
|
||||
|
||||
public class ZoomOutAAction extends DockingAction {
|
||||
|
||||
private final ImageIcon ICON = ResourceManager.loadImage("images/zoom_out.png");
|
||||
|
||||
private MemviewProvider provider;
|
||||
|
||||
public ZoomOutAAction(MemviewProvider provider) {
|
||||
super("Zoom Out (Addrs)", provider.getName());
|
||||
this.provider = provider;
|
||||
setEnabled(true);
|
||||
|
||||
this.setToolBarData(new ToolBarData(ICON, "aoverview"));
|
||||
|
||||
setDescription("Zoom Out (A)");
|
||||
setHelpLocation(new HelpLocation("DebuggerMemviewPlugin", "zoom"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
provider.changeZoomA(-1);
|
||||
provider.refresh();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/* ###
|
||||
* 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.memview.actions;
|
||||
|
||||
import javax.swing.ImageIcon;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.DockingAction;
|
||||
import docking.action.ToolBarData;
|
||||
import ghidra.app.plugin.core.debug.gui.memview.MemviewProvider;
|
||||
import ghidra.util.HelpLocation;
|
||||
import resources.ResourceManager;
|
||||
|
||||
public class ZoomOutTAction extends DockingAction {
|
||||
|
||||
private final ImageIcon ICON = ResourceManager.loadImage("images/zoom_out.png");
|
||||
|
||||
private MemviewProvider provider;
|
||||
|
||||
public ZoomOutTAction(MemviewProvider provider) {
|
||||
super("Zoom Out (Time)", provider.getName());
|
||||
this.provider = provider;
|
||||
setEnabled(true);
|
||||
|
||||
this.setToolBarData(new ToolBarData(ICON, "aoverview"));
|
||||
|
||||
setDescription("Zoom Out (T)");
|
||||
setHelpLocation(new HelpLocation("DebuggerMemviewPlugin", "zoom"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
provider.changeZoomT(-1);
|
||||
provider.refresh();
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user