GP-432: Implementing dynamic memory search. Fixing actions to bind to focused listing.

This commit is contained in:
Dan 2021-05-19 12:10:47 -04:00
parent 22675e9f39
commit fc885e7837
15 changed files with 268 additions and 83 deletions

View file

@ -34,8 +34,12 @@ import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMap;
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree;
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.AbstractDBTraceAddressSnapRangePropertyMapData;
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.TraceAddressSnapRangeQuery;
import ghidra.trace.database.space.DBTraceSpaceKey;
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.util.TraceChangeRecord;
import ghidra.util.LockHold;
import ghidra.util.database.*;
import ghidra.util.database.annot.*;
@ -49,7 +53,7 @@ public class DBTraceCommentAdapter
protected static final int MAX_COMMENT_TYPE = CodeUnit.REPEATABLE_COMMENT;
@DBAnnotatedObjectInfo(version = 0)
protected static class DBTraceCommentEntry
public static class DBTraceCommentEntry
extends AbstractDBTraceAddressSnapRangePropertyMapData<DBTraceCommentEntry> {
static final String TYPE_COLUMN_NAME = "Type";
static final String COMMENT_COLUMN_NAME = "Comment";
@ -89,6 +93,10 @@ public class DBTraceCommentAdapter
void setLifespan(Range<Long> lifespan) {
super.doSetLifespan(lifespan);
}
public int getType() {
return type;
}
}
public DBTraceCommentAdapter(DBHandle dbh, DBOpenMode openMode, ReadWriteLock lock,
@ -106,10 +114,15 @@ public class DBTraceCommentAdapter
if (commentType < MIN_COMMENT_TYPE || commentType > MAX_COMMENT_TYPE) {
throw new IllegalArgumentException("commentType");
}
String oldValue = null;
try (LockHold hold = LockHold.lock(lock.writeLock())) {
for (DBTraceCommentEntry entry : reduce(TraceAddressSnapRangeQuery.intersecting(
new AddressRangeImpl(address, address), lifespan)).values()) {
if (entry.type == commentType) {
if (lifespan.hasLowerBound() &&
entry.getLifespan().contains(lifespan.lowerEndpoint())) {
oldValue = entry.comment;
}
makeWay(entry, lifespan);
}
}
@ -118,6 +131,11 @@ public class DBTraceCommentAdapter
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) {

View file

@ -15,8 +15,6 @@
*/
package ghidra.trace.database.memory;
import static ghidra.lifecycle.Unfinished.TODO;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@ -47,8 +45,8 @@ import ghidra.trace.model.Trace.*;
import ghidra.trace.model.memory.*;
import ghidra.trace.util.TraceChangeRecord;
import ghidra.trace.util.TraceViewportSpanIterator;
import ghidra.util.LockHold;
import ghidra.util.MathUtilities;
import ghidra.util.*;
import ghidra.util.AddressIteratorAdapter;
import ghidra.util.database.*;
import ghidra.util.database.spatial.rect.Rectangle2DDirection;
import ghidra.util.exception.DuplicateNameException;
@ -758,10 +756,59 @@ public class DBTraceMemorySpace implements Unfinished, TraceMemorySpace, DBTrace
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
public Address findBytes(long snap, AddressRange range, ByteBuffer data, ByteBuffer mask,
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

View file

@ -424,8 +424,10 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
@Override
public AddressIterator getCommentAddressIterator(int commentType, AddressSetView addrSet,
boolean forward) {
// TODO Auto-generated method stub
return null;
return new IntersectionAddressSetView(addrSet, program.viewport.unionedAddresses(
s -> program.trace.getCommentAdapter()
.getAddressSetView(Range.singleton(s), e -> e.getType() == commentType)))
.getAddresses(forward);
}
@Override

View file

@ -240,8 +240,10 @@ public abstract class AbstractDBTraceProgramViewMemory
ByteBuffer bufBytes = ByteBuffer.wrap(bytes);
ByteBuffer bufMasks = masks == null ? null : ByteBuffer.wrap(masks);
Address minAddr = forward ? startAddr : endAddr;
Address maxAddr = forward ? endAddr : startAddr;
Iterator<AddressRange> it =
program.getAddressFactory().getAddressSet(startAddr, endAddr).iterator(forward);
program.getAddressFactory().getAddressSet(minAddr, maxAddr).iterator(forward);
while (it.hasNext()) {
AddressRange range = it.next();
DBTraceMemorySpace space = memoryManager.getMemorySpace(range.getAddressSpace(), false);

View file

@ -110,6 +110,12 @@ public class DBTraceProgramView implements TraceProgramView {
listenFor(TraceCodeChangeType.FRAGMENT_CHANGED, this::codeFragmentChanged);
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.LIFESPAN_CHANGED,
this::compositeLifespanChanged);
@ -328,6 +334,48 @@ public class DBTraceProgramView implements TraceProgramView {
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,
TraceData oldIsNull, TraceData added) {
DomainObjectEventQueues queues = isCodeVisible(space, added);

View file

@ -15,6 +15,8 @@
*/
package ghidra.trace.model;
import java.util.*;
import javax.swing.ImageIcon;
import com.google.common.collect.Range;
@ -106,6 +108,33 @@ public interface Trace extends DataTypeManagerDomainObject {
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>
extends DefaultTraceChangeType<T, U> {
public static final TraceCompositeDataChangeType<TraceAddressSnapRange, TraceData> ADDED =

View file

@ -418,7 +418,7 @@ public interface TraceMemoryOperations {
* @param snap the time to search
* @param range the address range to search
* @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
* the highest address.
* @param monitor a monitor for progress reporting and canceling

View file

@ -42,6 +42,7 @@ import ghidra.trace.model.TraceAddressSnapRange;
import ghidra.trace.model.memory.TraceMemoryState;
import ghidra.util.database.*;
import ghidra.util.task.ConsoleTaskMonitor;
import ghidra.util.task.TaskMonitor;
public abstract class AbstractDBTraceMemoryManagerTest
extends AbstractGhidraHeadlessIntegrationTest {
@ -715,6 +716,66 @@ public abstract class AbstractDBTraceMemoryManagerTest
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
public void testRemoveBytes() {
try (UndoableTransaction tid = UndoableTransaction.start(trace, "Testing", true)) {