mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
GP-432: Implementing dynamic memory search. Fixing actions to bind to focused listing.
This commit is contained in:
parent
22675e9f39
commit
fc885e7837
15 changed files with 268 additions and 83 deletions
|
@ -26,7 +26,7 @@ import com.google.protobuf.*;
|
||||||
import com.google.protobuf.CodedOutputStream.OutOfSpaceException;
|
import com.google.protobuf.CodedOutputStream.OutOfSpaceException;
|
||||||
|
|
||||||
import ghidra.async.*;
|
import ghidra.async.*;
|
||||||
import ghidra.comm.util.ByteBufferUtils;
|
import ghidra.util.ByteBufferUtils;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
|
|
||||||
public class AsyncProtobufMessageChannel<S extends GeneratedMessageV3, R extends GeneratedMessageV3> {
|
public class AsyncProtobufMessageChannel<S extends GeneratedMessageV3, R extends GeneratedMessageV3> {
|
||||||
|
|
|
@ -381,10 +381,13 @@ public class DebuggerListingProvider extends CodeViewerProvider implements Listi
|
||||||
protected final ForStaticSyncMappingChangeListener mappingChangeListener =
|
protected final ForStaticSyncMappingChangeListener mappingChangeListener =
|
||||||
new ForStaticSyncMappingChangeListener();
|
new ForStaticSyncMappingChangeListener();
|
||||||
|
|
||||||
|
protected final boolean isMainListing;
|
||||||
|
|
||||||
public DebuggerListingProvider(DebuggerListingPlugin plugin, FormatManager formatManager,
|
public DebuggerListingProvider(DebuggerListingPlugin plugin, FormatManager formatManager,
|
||||||
boolean isConnected) {
|
boolean isConnected) {
|
||||||
super(plugin, formatManager, isConnected);
|
super(plugin, formatManager, isConnected);
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
|
this.isMainListing = isConnected;
|
||||||
|
|
||||||
goToDialog = new DebuggerGoToDialog(this);
|
goToDialog = new DebuggerGoToDialog(this);
|
||||||
|
|
||||||
|
@ -420,6 +423,34 @@ public class DebuggerListingProvider extends CodeViewerProvider implements Listi
|
||||||
setHelpLocation(DebuggerResources.HELP_PROVIDER_LISTING);
|
setHelpLocation(DebuggerResources.HELP_PROVIDER_LISTING);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isConnected() {
|
||||||
|
/*
|
||||||
|
* NB. Other plugins ask isConnected meaning the main static listing. We don't want to be
|
||||||
|
* mistaken for it.
|
||||||
|
*/
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if this is the main dynamic listing.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The method {@link #isConnected()} is not quite the same as this, although the concepts are a
|
||||||
|
* little conflated, since before the debugger, no one else presented a listing that could claim
|
||||||
|
* to be "main" except the "connected" one. Here, we treat "connected" to mean that the address
|
||||||
|
* is synchronized exactly with the other providers. "Main" on the other hand, does not
|
||||||
|
* necessarily have that property, but it is still <em>not</em> a snapshot. It is the main
|
||||||
|
* listing presented by this plugin, and so it has certain unique features. Calling
|
||||||
|
* {@link DebuggerListingPlugin#getConnectedProvider()} will return the main dynamic listing,
|
||||||
|
* despite it not really being "connected."
|
||||||
|
*
|
||||||
|
* @return true if this is the main listing for the plugin.
|
||||||
|
*/
|
||||||
|
public boolean isMainListing() {
|
||||||
|
return isMainListing;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getWindowGroup() {
|
public String getWindowGroup() {
|
||||||
//TODO: Overriding this to align disconnected providers
|
//TODO: Overriding this to align disconnected providers
|
||||||
|
@ -428,7 +459,7 @@ public class DebuggerListingProvider extends CodeViewerProvider implements Listi
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeDataState(SaveState saveState) {
|
public void writeDataState(SaveState saveState) {
|
||||||
if (!isConnected()) {
|
if (!isMainListing()) {
|
||||||
current.writeDataState(tool, saveState, KEY_DEBUGGER_COORDINATES);
|
current.writeDataState(tool, saveState, KEY_DEBUGGER_COORDINATES);
|
||||||
}
|
}
|
||||||
super.writeDataState(saveState);
|
super.writeDataState(saveState);
|
||||||
|
@ -436,7 +467,7 @@ public class DebuggerListingProvider extends CodeViewerProvider implements Listi
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void readDataState(SaveState saveState) {
|
public void readDataState(SaveState saveState) {
|
||||||
if (!isConnected()) {
|
if (!isMainListing()) {
|
||||||
DebuggerCoordinates coordinates =
|
DebuggerCoordinates coordinates =
|
||||||
DebuggerCoordinates.readDataState(tool, saveState, KEY_DEBUGGER_COORDINATES, true);
|
DebuggerCoordinates.readDataState(tool, saveState, KEY_DEBUGGER_COORDINATES, true);
|
||||||
coordinatesActivated(coordinates);
|
coordinatesActivated(coordinates);
|
||||||
|
@ -466,7 +497,7 @@ public class DebuggerListingProvider extends CodeViewerProvider implements Listi
|
||||||
CONFIG_STATE_HANDLER.readConfigState(this, saveState);
|
CONFIG_STATE_HANDLER.readConfigState(this, saveState);
|
||||||
|
|
||||||
actionTrackLocation.setCurrentActionStateByUserData(trackingSpec);
|
actionTrackLocation.setCurrentActionStateByUserData(trackingSpec);
|
||||||
if (isConnected()) {
|
if (isMainListing()) {
|
||||||
actionSyncToStaticListing.setSelected(syncToStaticListing);
|
actionSyncToStaticListing.setSelected(syncToStaticListing);
|
||||||
followsCurrentThread = true;
|
followsCurrentThread = true;
|
||||||
}
|
}
|
||||||
|
@ -549,7 +580,7 @@ public class DebuggerListingProvider extends CodeViewerProvider implements Listi
|
||||||
this.markerService = markerService;
|
this.markerService = markerService;
|
||||||
createNewStaticTrackingMarker();
|
createNewStaticTrackingMarker();
|
||||||
|
|
||||||
if (this.markerService != null && !isConnected()) {
|
if (this.markerService != null && !isMainListing()) {
|
||||||
// NOTE: Connected provider marker listener is taken care of by CodeBrowserPlugin
|
// NOTE: Connected provider marker listener is taken care of by CodeBrowserPlugin
|
||||||
this.markerService.addChangeListener(markerChangeListener);
|
this.markerService.addChangeListener(markerChangeListener);
|
||||||
}
|
}
|
||||||
|
@ -735,7 +766,7 @@ public class DebuggerListingProvider extends CodeViewerProvider implements Listi
|
||||||
.onAction(this::activatedGoTo)
|
.onAction(this::activatedGoTo)
|
||||||
.buildAndInstallLocal(this);
|
.buildAndInstallLocal(this);
|
||||||
|
|
||||||
if (isConnected()) {
|
if (isMainListing()) {
|
||||||
actionSyncToStaticListing = new SyncToStaticListingAction();
|
actionSyncToStaticListing = new SyncToStaticListingAction();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -999,7 +1030,7 @@ public class DebuggerListingProvider extends CodeViewerProvider implements Listi
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSyncToStaticListing(boolean sync) {
|
public void setSyncToStaticListing(boolean sync) {
|
||||||
if (!isConnected()) {
|
if (!isMainListing()) {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"Only the main dynamic listing can be synced to the main static listing");
|
"Only the main dynamic listing can be synced to the main static listing");
|
||||||
}
|
}
|
||||||
|
@ -1018,7 +1049,7 @@ public class DebuggerListingProvider extends CodeViewerProvider implements Listi
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFollowsCurrentThread(boolean follows) {
|
public void setFollowsCurrentThread(boolean follows) {
|
||||||
if (isConnected()) {
|
if (isMainListing()) {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"The main dynamic listing always follows the current trace and thread");
|
"The main dynamic listing always follows the current trace and thread");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,54 +0,0 @@
|
||||||
/* ###
|
|
||||||
* IP: GHIDRA
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package ghidra.comm.util;
|
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Some utilities for manipulating a {@link ByteBuffer}
|
|
||||||
*/
|
|
||||||
public interface ByteBufferUtils {
|
|
||||||
/**
|
|
||||||
* Resize a write-mode buffer
|
|
||||||
*
|
|
||||||
* This preserves the buffer contents
|
|
||||||
*
|
|
||||||
* @param buf the buffer
|
|
||||||
* @param capacity the new capacity, greater or equal to the buffer's limit
|
|
||||||
* @return the new buffer
|
|
||||||
*/
|
|
||||||
public static ByteBuffer resize(ByteBuffer buf, int capacity) {
|
|
||||||
if (capacity < buf.limit()) {
|
|
||||||
throw new IllegalArgumentException("New capacity must fit current contents");
|
|
||||||
}
|
|
||||||
buf.flip();
|
|
||||||
ByteBuffer resized = ByteBuffer.allocate(capacity);
|
|
||||||
resized.put(buf);
|
|
||||||
return resized;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resize a write-mode buffer to twice its current capacity
|
|
||||||
*
|
|
||||||
* This preserves the buffer contents
|
|
||||||
*
|
|
||||||
* @param buf the buffer
|
|
||||||
* @return the new buffer
|
|
||||||
*/
|
|
||||||
public static ByteBuffer upsize(ByteBuffer buf) {
|
|
||||||
return resize(buf, buf.capacity() * 2);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -34,8 +34,12 @@ import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMap;
|
||||||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree;
|
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree;
|
||||||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.AbstractDBTraceAddressSnapRangePropertyMapData;
|
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.AbstractDBTraceAddressSnapRangePropertyMapData;
|
||||||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.TraceAddressSnapRangeQuery;
|
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.TraceAddressSnapRangeQuery;
|
||||||
|
import ghidra.trace.database.space.DBTraceSpaceKey;
|
||||||
import ghidra.trace.database.thread.DBTraceThreadManager;
|
import ghidra.trace.database.thread.DBTraceThreadManager;
|
||||||
|
import ghidra.trace.model.ImmutableTraceAddressSnapRange;
|
||||||
|
import ghidra.trace.model.Trace.TraceCommentChangeType;
|
||||||
import ghidra.trace.model.TraceAddressSnapRange;
|
import ghidra.trace.model.TraceAddressSnapRange;
|
||||||
|
import ghidra.trace.util.TraceChangeRecord;
|
||||||
import ghidra.util.LockHold;
|
import ghidra.util.LockHold;
|
||||||
import ghidra.util.database.*;
|
import ghidra.util.database.*;
|
||||||
import ghidra.util.database.annot.*;
|
import ghidra.util.database.annot.*;
|
||||||
|
@ -49,7 +53,7 @@ public class DBTraceCommentAdapter
|
||||||
protected static final int MAX_COMMENT_TYPE = CodeUnit.REPEATABLE_COMMENT;
|
protected static final int MAX_COMMENT_TYPE = CodeUnit.REPEATABLE_COMMENT;
|
||||||
|
|
||||||
@DBAnnotatedObjectInfo(version = 0)
|
@DBAnnotatedObjectInfo(version = 0)
|
||||||
protected static class DBTraceCommentEntry
|
public static class DBTraceCommentEntry
|
||||||
extends AbstractDBTraceAddressSnapRangePropertyMapData<DBTraceCommentEntry> {
|
extends AbstractDBTraceAddressSnapRangePropertyMapData<DBTraceCommentEntry> {
|
||||||
static final String TYPE_COLUMN_NAME = "Type";
|
static final String TYPE_COLUMN_NAME = "Type";
|
||||||
static final String COMMENT_COLUMN_NAME = "Comment";
|
static final String COMMENT_COLUMN_NAME = "Comment";
|
||||||
|
@ -89,6 +93,10 @@ public class DBTraceCommentAdapter
|
||||||
void setLifespan(Range<Long> lifespan) {
|
void setLifespan(Range<Long> lifespan) {
|
||||||
super.doSetLifespan(lifespan);
|
super.doSetLifespan(lifespan);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public DBTraceCommentAdapter(DBHandle dbh, DBOpenMode openMode, ReadWriteLock lock,
|
public DBTraceCommentAdapter(DBHandle dbh, DBOpenMode openMode, ReadWriteLock lock,
|
||||||
|
@ -106,10 +114,15 @@ public class DBTraceCommentAdapter
|
||||||
if (commentType < MIN_COMMENT_TYPE || commentType > MAX_COMMENT_TYPE) {
|
if (commentType < MIN_COMMENT_TYPE || commentType > MAX_COMMENT_TYPE) {
|
||||||
throw new IllegalArgumentException("commentType");
|
throw new IllegalArgumentException("commentType");
|
||||||
}
|
}
|
||||||
|
String oldValue = null;
|
||||||
try (LockHold hold = LockHold.lock(lock.writeLock())) {
|
try (LockHold hold = LockHold.lock(lock.writeLock())) {
|
||||||
for (DBTraceCommentEntry entry : reduce(TraceAddressSnapRangeQuery.intersecting(
|
for (DBTraceCommentEntry entry : reduce(TraceAddressSnapRangeQuery.intersecting(
|
||||||
new AddressRangeImpl(address, address), lifespan)).values()) {
|
new AddressRangeImpl(address, address), lifespan)).values()) {
|
||||||
if (entry.type == commentType) {
|
if (entry.type == commentType) {
|
||||||
|
if (lifespan.hasLowerBound() &&
|
||||||
|
entry.getLifespan().contains(lifespan.lowerEndpoint())) {
|
||||||
|
oldValue = entry.comment;
|
||||||
|
}
|
||||||
makeWay(entry, lifespan);
|
makeWay(entry, lifespan);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -118,6 +131,11 @@ public class DBTraceCommentAdapter
|
||||||
entry.set((byte) commentType, comment);
|
entry.set((byte) commentType, comment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
trace.setChanged(new TraceChangeRecord<TraceAddressSnapRange, String>(
|
||||||
|
TraceCommentChangeType.byType(commentType),
|
||||||
|
DBTraceSpaceKey.create(address.getAddressSpace(), null, 0),
|
||||||
|
new ImmutableTraceAddressSnapRange(address, lifespan),
|
||||||
|
oldValue, comment));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String commentFromArray(String[] comment) {
|
public static String commentFromArray(String[] comment) {
|
||||||
|
|
|
@ -15,8 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.trace.database.memory;
|
package ghidra.trace.database.memory;
|
||||||
|
|
||||||
import static ghidra.lifecycle.Unfinished.TODO;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
|
@ -47,8 +45,8 @@ import ghidra.trace.model.Trace.*;
|
||||||
import ghidra.trace.model.memory.*;
|
import ghidra.trace.model.memory.*;
|
||||||
import ghidra.trace.util.TraceChangeRecord;
|
import ghidra.trace.util.TraceChangeRecord;
|
||||||
import ghidra.trace.util.TraceViewportSpanIterator;
|
import ghidra.trace.util.TraceViewportSpanIterator;
|
||||||
import ghidra.util.LockHold;
|
import ghidra.util.*;
|
||||||
import ghidra.util.MathUtilities;
|
import ghidra.util.AddressIteratorAdapter;
|
||||||
import ghidra.util.database.*;
|
import ghidra.util.database.*;
|
||||||
import ghidra.util.database.spatial.rect.Rectangle2DDirection;
|
import ghidra.util.database.spatial.rect.Rectangle2DDirection;
|
||||||
import ghidra.util.exception.DuplicateNameException;
|
import ghidra.util.exception.DuplicateNameException;
|
||||||
|
@ -758,10 +756,59 @@ public class DBTraceMemorySpace implements Unfinished, TraceMemorySpace, DBTrace
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Address doFindBytesInRange(long snap, AddressRange range, ByteBuffer data,
|
||||||
|
ByteBuffer mask, boolean forward, TaskMonitor monitor) {
|
||||||
|
int len = data.capacity();
|
||||||
|
AddressRange rangeOfStarts =
|
||||||
|
new AddressRangeImpl(range.getMinAddress(), range.getMaxAddress().subtract(len - 1));
|
||||||
|
ByteBuffer read = ByteBuffer.allocate(len);
|
||||||
|
for (Address addr : AddressIteratorAdapter.forRange(rangeOfStarts, forward)) {
|
||||||
|
monitor.incrementProgress(1);
|
||||||
|
if (monitor.isCancelled()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
read.clear();
|
||||||
|
int l = getBytes(snap, addr, read);
|
||||||
|
if (l != len) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!ByteBufferUtils.maskedEquals(mask, data, read)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Address findBytes(long snap, AddressRange range, ByteBuffer data, ByteBuffer mask,
|
public Address findBytes(long snap, AddressRange range, ByteBuffer data, ByteBuffer mask,
|
||||||
boolean forward, TaskMonitor monitor) {
|
boolean forward, TaskMonitor monitor) {
|
||||||
return TODO();
|
// ProgramDB uses the naive method with some skipping, so here we go....
|
||||||
|
// TODO: This could be made faster by skipping over non-initialized blocks
|
||||||
|
// TODO: DFA method would be complicated by masks....
|
||||||
|
int len = data.capacity();
|
||||||
|
if (mask != null && mask.capacity() != len) {
|
||||||
|
throw new IllegalArgumentException("data and mask must have same capacity");
|
||||||
|
}
|
||||||
|
if (len == 0 || range.getLength() > 0 && range.getLength() < len) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Could do better, but have to worry about viewport, too
|
||||||
|
// This will reduce the search to ranges that have been written at any snap
|
||||||
|
// We could do for this and previous snaps, but that's where the viewport comes in.
|
||||||
|
// TODO: Potentially costly to pre-compute the set concretely
|
||||||
|
AddressSet known = new AddressSet(
|
||||||
|
stateMapSpace.getAddressSetView(Range.all(), s -> s == TraceMemoryState.KNOWN))
|
||||||
|
.intersect(new AddressSet(range));
|
||||||
|
monitor.initialize(known.getNumAddresses());
|
||||||
|
for (AddressRange knownRange : known.getAddressRanges(forward)) {
|
||||||
|
Address found = doFindBytesInRange(snap, knownRange, data, mask, forward, monitor);
|
||||||
|
if (found != null) {
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Test this
|
// TODO: Test this
|
||||||
|
|
|
@ -424,8 +424,10 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
|
||||||
@Override
|
@Override
|
||||||
public AddressIterator getCommentAddressIterator(int commentType, AddressSetView addrSet,
|
public AddressIterator getCommentAddressIterator(int commentType, AddressSetView addrSet,
|
||||||
boolean forward) {
|
boolean forward) {
|
||||||
// TODO Auto-generated method stub
|
return new IntersectionAddressSetView(addrSet, program.viewport.unionedAddresses(
|
||||||
return null;
|
s -> program.trace.getCommentAdapter()
|
||||||
|
.getAddressSetView(Range.singleton(s), e -> e.getType() == commentType)))
|
||||||
|
.getAddresses(forward);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -240,8 +240,10 @@ public abstract class AbstractDBTraceProgramViewMemory
|
||||||
ByteBuffer bufBytes = ByteBuffer.wrap(bytes);
|
ByteBuffer bufBytes = ByteBuffer.wrap(bytes);
|
||||||
ByteBuffer bufMasks = masks == null ? null : ByteBuffer.wrap(masks);
|
ByteBuffer bufMasks = masks == null ? null : ByteBuffer.wrap(masks);
|
||||||
|
|
||||||
|
Address minAddr = forward ? startAddr : endAddr;
|
||||||
|
Address maxAddr = forward ? endAddr : startAddr;
|
||||||
Iterator<AddressRange> it =
|
Iterator<AddressRange> it =
|
||||||
program.getAddressFactory().getAddressSet(startAddr, endAddr).iterator(forward);
|
program.getAddressFactory().getAddressSet(minAddr, maxAddr).iterator(forward);
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
AddressRange range = it.next();
|
AddressRange range = it.next();
|
||||||
DBTraceMemorySpace space = memoryManager.getMemorySpace(range.getAddressSpace(), false);
|
DBTraceMemorySpace space = memoryManager.getMemorySpace(range.getAddressSpace(), false);
|
||||||
|
|
|
@ -110,6 +110,12 @@ public class DBTraceProgramView implements TraceProgramView {
|
||||||
listenFor(TraceCodeChangeType.FRAGMENT_CHANGED, this::codeFragmentChanged);
|
listenFor(TraceCodeChangeType.FRAGMENT_CHANGED, this::codeFragmentChanged);
|
||||||
listenFor(TraceCodeChangeType.DATA_TYPE_REPLACED, this::codeDataTypeReplaced);
|
listenFor(TraceCodeChangeType.DATA_TYPE_REPLACED, this::codeDataTypeReplaced);
|
||||||
|
|
||||||
|
listenFor(TraceCommentChangeType.EOL_CHANGED, this::commentEolChanged);
|
||||||
|
listenFor(TraceCommentChangeType.PLATE_CHANGED, this::commentPlateChanged);
|
||||||
|
listenFor(TraceCommentChangeType.POST_CHANGED, this::commentPostChanged);
|
||||||
|
listenFor(TraceCommentChangeType.PRE_CHANGED, this::commentPreChanged);
|
||||||
|
listenFor(TraceCommentChangeType.REPEATABLE_CHANGED, this::commentRepeatableChanged);
|
||||||
|
|
||||||
listenFor(TraceCompositeDataChangeType.ADDED, this::compositeDataAdded);
|
listenFor(TraceCompositeDataChangeType.ADDED, this::compositeDataAdded);
|
||||||
listenFor(TraceCompositeDataChangeType.LIFESPAN_CHANGED,
|
listenFor(TraceCompositeDataChangeType.LIFESPAN_CHANGED,
|
||||||
this::compositeLifespanChanged);
|
this::compositeLifespanChanged);
|
||||||
|
@ -328,6 +334,48 @@ public class DBTraceProgramView implements TraceProgramView {
|
||||||
range.getX1(), range.getX2(), null, null, null));
|
range.getX1(), range.getX2(), null, null, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void commentChanged(int docrType, TraceAddressSpace space,
|
||||||
|
TraceAddressSnapRange range,
|
||||||
|
String oldValue, String newValue) {
|
||||||
|
DomainObjectEventQueues queues = isVisible(space, range);
|
||||||
|
if (queues == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
queues.fireEvent(new ProgramChangeRecord(docrType,
|
||||||
|
range.getX1(), range.getX2(), null, oldValue, newValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void commentEolChanged(TraceAddressSpace space, TraceAddressSnapRange range,
|
||||||
|
String oldValue, String newValue) {
|
||||||
|
commentChanged(ChangeManager.DOCR_EOL_COMMENT_CHANGED, space, range, oldValue,
|
||||||
|
newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void commentPlateChanged(TraceAddressSpace space, TraceAddressSnapRange range,
|
||||||
|
String oldValue, String newValue) {
|
||||||
|
commentChanged(ChangeManager.DOCR_PLATE_COMMENT_CHANGED, space, range, oldValue,
|
||||||
|
newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void commentPostChanged(TraceAddressSpace space, TraceAddressSnapRange range,
|
||||||
|
String oldValue, String newValue) {
|
||||||
|
commentChanged(ChangeManager.DOCR_POST_COMMENT_CHANGED, space, range, oldValue,
|
||||||
|
newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void commentPreChanged(TraceAddressSpace space, TraceAddressSnapRange range,
|
||||||
|
String oldValue, String newValue) {
|
||||||
|
commentChanged(ChangeManager.DOCR_PRE_COMMENT_CHANGED, space, range, oldValue,
|
||||||
|
newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void commentRepeatableChanged(TraceAddressSpace space, TraceAddressSnapRange range,
|
||||||
|
String oldValue, String newValue) {
|
||||||
|
// TODO: The "repeatable" semantics are not implemented, yet.
|
||||||
|
commentChanged(ChangeManager.DOCR_REPEATABLE_COMMENT_CHANGED, space, range, oldValue,
|
||||||
|
newValue);
|
||||||
|
}
|
||||||
|
|
||||||
private void compositeDataAdded(TraceAddressSpace space, TraceAddressSnapRange range,
|
private void compositeDataAdded(TraceAddressSpace space, TraceAddressSnapRange range,
|
||||||
TraceData oldIsNull, TraceData added) {
|
TraceData oldIsNull, TraceData added) {
|
||||||
DomainObjectEventQueues queues = isCodeVisible(space, added);
|
DomainObjectEventQueues queues = isCodeVisible(space, added);
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.trace.model;
|
package ghidra.trace.model;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
|
|
||||||
import com.google.common.collect.Range;
|
import com.google.common.collect.Range;
|
||||||
|
@ -106,6 +108,33 @@ public interface Trace extends DataTypeManagerDomainObject {
|
||||||
new TraceCodeChangeType<>();
|
new TraceCodeChangeType<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static final class TraceCommentChangeType
|
||||||
|
extends DefaultTraceChangeType<TraceAddressSnapRange, String> {
|
||||||
|
private static final Map<Integer, TraceCommentChangeType> BY_TYPE = new HashMap<>();
|
||||||
|
|
||||||
|
public static final TraceCommentChangeType PLATE_CHANGED =
|
||||||
|
new TraceCommentChangeType(CodeUnit.PLATE_COMMENT);
|
||||||
|
public static final TraceCommentChangeType PRE_CHANGED =
|
||||||
|
new TraceCommentChangeType(CodeUnit.PRE_COMMENT);
|
||||||
|
public static final TraceCommentChangeType POST_CHANGED =
|
||||||
|
new TraceCommentChangeType(CodeUnit.POST_COMMENT);
|
||||||
|
public static final TraceCommentChangeType EOL_CHANGED =
|
||||||
|
new TraceCommentChangeType(CodeUnit.EOL_COMMENT);
|
||||||
|
public static final TraceCommentChangeType REPEATABLE_CHANGED =
|
||||||
|
new TraceCommentChangeType(CodeUnit.REPEATABLE_COMMENT);
|
||||||
|
|
||||||
|
public static TraceCommentChangeType byType(int type) {
|
||||||
|
return Objects.requireNonNull(BY_TYPE.get(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
public final int type;
|
||||||
|
|
||||||
|
private TraceCommentChangeType(int type) {
|
||||||
|
this.type = type;
|
||||||
|
BY_TYPE.put(type, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static final class TraceCompositeDataChangeType<T, U>
|
public static final class TraceCompositeDataChangeType<T, U>
|
||||||
extends DefaultTraceChangeType<T, U> {
|
extends DefaultTraceChangeType<T, U> {
|
||||||
public static final TraceCompositeDataChangeType<TraceAddressSnapRange, TraceData> ADDED =
|
public static final TraceCompositeDataChangeType<TraceAddressSnapRange, TraceData> ADDED =
|
||||||
|
|
|
@ -418,7 +418,7 @@ public interface TraceMemoryOperations {
|
||||||
* @param snap the time to search
|
* @param snap the time to search
|
||||||
* @param range the address range to search
|
* @param range the address range to search
|
||||||
* @param data the values to search for
|
* @param data the values to search for
|
||||||
* @param mask a mask on the bits of {@code data}
|
* @param mask a mask on the bits of {@code data}; or null to match all bytes exactly
|
||||||
* @param forward true to return the match with the lowest address in {@code range}, false for
|
* @param forward true to return the match with the lowest address in {@code range}, false for
|
||||||
* the highest address.
|
* the highest address.
|
||||||
* @param monitor a monitor for progress reporting and canceling
|
* @param monitor a monitor for progress reporting and canceling
|
||||||
|
|
|
@ -42,6 +42,7 @@ import ghidra.trace.model.TraceAddressSnapRange;
|
||||||
import ghidra.trace.model.memory.TraceMemoryState;
|
import ghidra.trace.model.memory.TraceMemoryState;
|
||||||
import ghidra.util.database.*;
|
import ghidra.util.database.*;
|
||||||
import ghidra.util.task.ConsoleTaskMonitor;
|
import ghidra.util.task.ConsoleTaskMonitor;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
public abstract class AbstractDBTraceMemoryManagerTest
|
public abstract class AbstractDBTraceMemoryManagerTest
|
||||||
extends AbstractGhidraHeadlessIntegrationTest {
|
extends AbstractGhidraHeadlessIntegrationTest {
|
||||||
|
@ -715,6 +716,66 @@ public abstract class AbstractDBTraceMemoryManagerTest
|
||||||
assertArrayEquals(arr(1, 2, 3, 4), read.array());
|
assertArrayEquals(arr(1, 2, 3, 4), read.array());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFindBytes() {
|
||||||
|
try (UndoableTransaction tid = UndoableTransaction.start(trace, "Testing", true)) {
|
||||||
|
assertEquals(4, memory.putBytes(3, addr(0x4000), buf(1, 2, 3, 4)));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
memory.findBytes(3, range(0x4000, 0x4003), buf(1, 2, 3, 4), buf(-1, -1, -1),
|
||||||
|
true, TaskMonitor.DUMMY);
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException e) {
|
||||||
|
// pass
|
||||||
|
}
|
||||||
|
|
||||||
|
// Degenerate
|
||||||
|
assertNull(
|
||||||
|
memory.findBytes(2, range(0x4000, 0x4003), buf(), buf(),
|
||||||
|
true, TaskMonitor.DUMMY));
|
||||||
|
|
||||||
|
// Too soon
|
||||||
|
assertNull(
|
||||||
|
memory.findBytes(2, range(0x4000, 0x4003), buf(1, 2, 3, 4), buf(-1, -1, -1, -1),
|
||||||
|
true, TaskMonitor.DUMMY));
|
||||||
|
|
||||||
|
// Too small
|
||||||
|
assertNull(
|
||||||
|
memory.findBytes(3, range(0x4000, 0x4002), buf(1, 2, 3, 4), buf(-1, -1, -1, -1),
|
||||||
|
true, TaskMonitor.DUMMY));
|
||||||
|
|
||||||
|
// Too high
|
||||||
|
assertNull(
|
||||||
|
memory.findBytes(3, range(0x4001, 0x4004), buf(1, 2, 3, 4), buf(-1, -1, -1, -1),
|
||||||
|
true, TaskMonitor.DUMMY));
|
||||||
|
|
||||||
|
// Too low
|
||||||
|
assertNull(
|
||||||
|
memory.findBytes(3, range(0x3fff, 0x4002), buf(1, 2, 3, 4), buf(-1, -1, -1, -1),
|
||||||
|
true, TaskMonitor.DUMMY));
|
||||||
|
|
||||||
|
// Perfect match
|
||||||
|
assertEquals(addr(0x4000),
|
||||||
|
memory.findBytes(3, range(0x4000, 0x4003), buf(1, 2, 3, 4), buf(-1, -1, -1, -1),
|
||||||
|
true, TaskMonitor.DUMMY));
|
||||||
|
|
||||||
|
// Make it work for the match
|
||||||
|
assertEquals(addr(0x4000),
|
||||||
|
memory.findBytes(3, range(0x0, -1), buf(1, 2, 3, 4), buf(-1, -1, -1, -1),
|
||||||
|
true, TaskMonitor.DUMMY));
|
||||||
|
|
||||||
|
// Make it work for the match
|
||||||
|
assertEquals(addr(0x4000),
|
||||||
|
memory.findBytes(3, range(0x0, -1), buf(1), buf(-1),
|
||||||
|
true, TaskMonitor.DUMMY));
|
||||||
|
|
||||||
|
// Sub match
|
||||||
|
assertEquals(addr(0x4001),
|
||||||
|
memory.findBytes(3, range(0x4000, 0x4003), buf(2, 3, 4), buf(-1, -1, -1),
|
||||||
|
true, TaskMonitor.DUMMY));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRemoveBytes() {
|
public void testRemoveBytes() {
|
||||||
try (UndoableTransaction tid = UndoableTransaction.start(trace, "Testing", true)) {
|
try (UndoableTransaction tid = UndoableTransaction.start(trace, "Testing", true)) {
|
||||||
|
|
|
@ -89,6 +89,11 @@ public class AddressIteratorAdapter extends NestedIterator<AddressRange, Address
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Iterable<Address> forRange(AddressRange range, boolean forward) {
|
||||||
|
return () -> forward ? new ForwardAddressIterator(range)
|
||||||
|
: new BackwardAddressIterator(range);
|
||||||
|
}
|
||||||
|
|
||||||
public AddressIteratorAdapter(Iterator<AddressRange> outer, boolean forward) {
|
public AddressIteratorAdapter(Iterator<AddressRange> outer, boolean forward) {
|
||||||
super(outer, forward ? ForwardAddressIterator::new : BackwardAddressIterator::new);
|
super(outer, forward ? ForwardAddressIterator::new : BackwardAddressIterator::new);
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,8 +62,7 @@ import ghidra.util.task.*;
|
||||||
import resources.ResourceManager;
|
import resources.ResourceManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plugin to search text as it is displayed in the fields of the
|
* Plugin to search text as it is displayed in the fields of the Code Browser.
|
||||||
* Code Browser.
|
|
||||||
*/
|
*/
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
@PluginInfo(
|
@PluginInfo(
|
||||||
|
@ -114,6 +113,7 @@ public class SearchTextPlugin extends ProgramPlugin implements OptionsChangeList
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The constructor for the SearchTextPlugin.
|
* The constructor for the SearchTextPlugin.
|
||||||
|
*
|
||||||
* @param plugintool The tool required by this plugin.
|
* @param plugintool The tool required by this plugin.
|
||||||
*/
|
*/
|
||||||
public SearchTextPlugin(PluginTool plugintool) {
|
public SearchTextPlugin(PluginTool plugintool) {
|
||||||
|
@ -260,10 +260,7 @@ public class SearchTextPlugin extends ProgramPlugin implements OptionsChangeList
|
||||||
}
|
}
|
||||||
|
|
||||||
private ProgramLocation getStartLocation() {
|
private ProgramLocation getStartLocation() {
|
||||||
if (currentLocation == null) {
|
return currentLocation = navigatable.getLocation();
|
||||||
currentLocation = navigatable.getLocation();
|
|
||||||
}
|
|
||||||
return currentLocation;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void searchNext(Program program, Navigatable searchNavigatable, Searcher textSearcher) {
|
private void searchNext(Program program, Navigatable searchNavigatable, Searcher textSearcher) {
|
||||||
|
@ -475,9 +472,8 @@ public class SearchTextPlugin extends ProgramPlugin implements OptionsChangeList
|
||||||
searchDialog.setHasSelection(context.hasSelection());
|
searchDialog.setHasSelection(context.hasSelection());
|
||||||
}
|
}
|
||||||
|
|
||||||
CodeViewerService codeViewerService = tool.getService(CodeViewerService.class);
|
|
||||||
String textSelection = navigatable.getTextSelection();
|
String textSelection = navigatable.getTextSelection();
|
||||||
ProgramLocation location = codeViewerService.getCurrentLocation();
|
ProgramLocation location = navigatable.getLocation();
|
||||||
Address address = location.getAddress();
|
Address address = location.getAddress();
|
||||||
Listing listing = context.getProgram().getListing();
|
Listing listing = context.getProgram().getListing();
|
||||||
CodeUnit codeUnit = listing.getCodeUnitAt(address);
|
CodeUnit codeUnit = listing.getCodeUnitAt(address);
|
||||||
|
@ -499,6 +495,7 @@ public class SearchTextPlugin extends ProgramPlugin implements OptionsChangeList
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the address set for the selection.
|
* Get the address set for the selection.
|
||||||
|
*
|
||||||
* @return null if there is no selection
|
* @return null if there is no selection
|
||||||
*/
|
*/
|
||||||
private AddressSetView getAddressSet(Navigatable searchNavigatable, SearchOptions options) {
|
private AddressSetView getAddressSet(Navigatable searchNavigatable, SearchOptions options) {
|
||||||
|
|
|
@ -75,7 +75,7 @@ public class InstructionMnemonicOperandFieldSearcher extends ProgramDatabaseFiel
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Address advance(List<ProgramLocation> currentMatches) {
|
protected Address advance(List<ProgramLocation> currentMatches) {
|
||||||
Instruction instruction = iterator.next();
|
Instruction instruction = iterator.hasNext() ? iterator.next() : null;
|
||||||
Address nextAddress = null;
|
Address nextAddress = null;
|
||||||
if (instruction != null) {
|
if (instruction != null) {
|
||||||
nextAddress = instruction.getMinAddress();
|
nextAddress = instruction.getMinAddress();
|
||||||
|
|
|
@ -84,7 +84,7 @@ public class TableComponentProvider<T> extends ComponentProviderAdapter
|
||||||
|
|
||||||
this.tableServicePlugin = plugin;
|
this.tableServicePlugin = plugin;
|
||||||
this.navigatable = navigatable;
|
this.navigatable = navigatable;
|
||||||
this.program = plugin.getProgram();
|
this.program = navigatable.getProgram();
|
||||||
this.model = model;
|
this.model = model;
|
||||||
this.programName = programName;
|
this.programName = programName;
|
||||||
this.markerService = markerService;
|
this.markerService = markerService;
|
||||||
|
@ -152,7 +152,6 @@ public class TableComponentProvider<T> extends ComponentProviderAdapter
|
||||||
selectAction = new MakeProgramSelectionAction(tableServicePlugin, table) {
|
selectAction = new MakeProgramSelectionAction(tableServicePlugin, table) {
|
||||||
@Override
|
@Override
|
||||||
protected ProgramSelection makeSelection(ActionContext context) {
|
protected ProgramSelection makeSelection(ActionContext context) {
|
||||||
|
|
||||||
ProgramSelection selection = table.getProgramSelection();
|
ProgramSelection selection = table.getProgramSelection();
|
||||||
navigatable.goTo(program, new ProgramLocation(program, selection.getMinAddress()));
|
navigatable.goTo(program, new ProgramLocation(program, selection.getMinAddress()));
|
||||||
navigatable.setSelection(selection);
|
navigatable.setSelection(selection);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue