mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 02:39:44 +02:00
GP-500: Added Memview (address x time object plot)
This commit is contained in:
parent
8fa549119d
commit
ea87c4e063
24 changed files with 2680 additions and 1 deletions
|
@ -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/DebuggerGoToDialog.png||GHIDRA||||END|
|
||||||
src/main/help/help/topics/DebuggerListingPlugin/images/DebuggerListingPlugin.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/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/DebuggerModelServicePlugin/DebuggerModelServicePlugin.html||GHIDRA||||END|
|
||||||
src/main/help/help/topics/DebuggerModulesPlugin/DebuggerModulesPlugin.html||GHIDRA||||END|
|
src/main/help/help/topics/DebuggerModulesPlugin/DebuggerModulesPlugin.html||GHIDRA||||END|
|
||||||
src/main/help/help/topics/DebuggerModulesPlugin/images/DebuggerModuleMapProposalDialog.png||GHIDRA||||END|
|
src/main/help/help/topics/DebuggerModulesPlugin/images/DebuggerModuleMapProposalDialog.png||GHIDRA||||END|
|
||||||
|
|
|
@ -132,8 +132,12 @@
|
||||||
sortgroup="n"
|
sortgroup="n"
|
||||||
target="help/topics/DebuggerWatchesPlugin/DebuggerWatchesPlugin.html" />
|
target="help/topics/DebuggerWatchesPlugin/DebuggerWatchesPlugin.html" />
|
||||||
|
|
||||||
<tocdef id="DebuggerBots" text="Bots: Workflow Automation"
|
<tocdef id="DebuggerMemviewPlugin" text="Memview Plot"
|
||||||
sortgroup="o"
|
sortgroup="o"
|
||||||
|
target="help/topics/DebuggerMemviewPlugin/DebuggerMemviewPlugin.html" />
|
||||||
|
|
||||||
|
<tocdef id="DebuggerBots" text="Bots: Workflow Automation"
|
||||||
|
sortgroup="p"
|
||||||
target="help/topics/DebuggerBots/DebuggerBots.html" />
|
target="help/topics/DebuggerBots/DebuggerBots.html" />
|
||||||
</tocdef>
|
</tocdef>
|
||||||
</tocref>
|
</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…
Add table
Add a link
Reference in a new issue