GP-500: Added Memview (address x time object plot)

This commit is contained in:
d-millar 2020-12-15 21:19:34 +00:00 committed by Dan
parent 8fa549119d
commit ea87c4e063
24 changed files with 2680 additions and 1 deletions

View File

@ -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);
}
}
}

View File

@ -42,6 +42,13 @@ src/main/help/help/topics/DebuggerListingPlugin/DebuggerListingPlugin.html||GHID
src/main/help/help/topics/DebuggerListingPlugin/images/DebuggerGoToDialog.png||GHIDRA||||END|
src/main/help/help/topics/DebuggerListingPlugin/images/DebuggerListingPlugin.png||GHIDRA||||END|
src/main/help/help/topics/DebuggerListingPlugin/images/DebuggerModuleImportDialog.png||GHIDRA||||END|
src/main/help/help/topics/DebuggerMemviewPlugin/DebuggerMemviewPlugin.html||GHIDRA||||END|
src/main/help/help/topics/DebuggerMemviewPlugin/images/DebuggerMemviewPlugin.png||GHIDRA||||END|
src/main/help/help/topics/DebuggerMemviewPlugin/images/filter_off.png||GHIDRA||||END|
src/main/help/help/topics/DebuggerMemviewPlugin/images/sync_enabled.png||GHIDRA||||END|
src/main/help/help/topics/DebuggerMemviewPlugin/images/view-refresh.png||Tango Icons - Public Domain|||tango icon set|END|
src/main/help/help/topics/DebuggerMemviewPlugin/images/zoom_in.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END|
src/main/help/help/topics/DebuggerMemviewPlugin/images/zoom_out.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END|
src/main/help/help/topics/DebuggerModelServicePlugin/DebuggerModelServicePlugin.html||GHIDRA||||END|
src/main/help/help/topics/DebuggerModulesPlugin/DebuggerModulesPlugin.html||GHIDRA||||END|
src/main/help/help/topics/DebuggerModulesPlugin/images/DebuggerModuleMapProposalDialog.png||GHIDRA||||END|

View File

@ -132,8 +132,12 @@
sortgroup="n"
target="help/topics/DebuggerWatchesPlugin/DebuggerWatchesPlugin.html" />
<tocdef id="DebuggerMemviewPlugin" text="Memview Plot"
sortgroup="o"
target="help/topics/DebuggerMemviewPlugin/DebuggerMemviewPlugin.html" />
<tocdef id="DebuggerBots" text="Bots: Workflow Automation"
sortgroup="o"
sortgroup="p"
target="help/topics/DebuggerBots/DebuggerBots.html" />
</tocdef>
</tocref>

View File

@ -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&amp;drop can also be used to
position the panel view. Ctrl-drag&amp;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

View File

@ -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);
}
}

View File

@ -0,0 +1,251 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}
}

View File

@ -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));
}
}

View File

@ -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();
});
}
}

View File

@ -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();
}

View File

@ -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);
});
}
*/
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}