mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 02:39:44 +02:00
Merge remote-tracking branch 'origin/GP-1386_Dan_DBTraceObjectModel-REBASED-1--SQUASHED'
This commit is contained in:
commit
8e59d0e673
179 changed files with 9971 additions and 797 deletions
|
@ -42,7 +42,6 @@ import ghidra.trace.database.language.DBTraceLanguageManager;
|
|||
import ghidra.trace.database.listing.DBTraceCodeManager;
|
||||
import ghidra.trace.database.listing.DBTraceCommentAdapter;
|
||||
import ghidra.trace.database.memory.DBTraceMemoryManager;
|
||||
import ghidra.trace.database.memory.DBTraceMemoryRegion;
|
||||
import ghidra.trace.database.module.DBTraceModuleManager;
|
||||
import ghidra.trace.database.module.DBTraceStaticMappingManager;
|
||||
import ghidra.trace.database.program.DBTraceProgramView;
|
||||
|
@ -50,9 +49,11 @@ import ghidra.trace.database.program.DBTraceVariableSnapProgramView;
|
|||
import ghidra.trace.database.property.DBTraceAddressPropertyManager;
|
||||
import ghidra.trace.database.stack.DBTraceStackManager;
|
||||
import ghidra.trace.database.symbol.*;
|
||||
import ghidra.trace.database.target.DBTraceObjectManager;
|
||||
import ghidra.trace.database.thread.DBTraceThreadManager;
|
||||
import ghidra.trace.database.time.DBTraceTimeManager;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.memory.TraceMemoryRegion;
|
||||
import ghidra.trace.util.TraceChangeManager;
|
||||
import ghidra.trace.util.TraceChangeRecord;
|
||||
import ghidra.util.*;
|
||||
|
@ -104,6 +105,8 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
|
|||
@DependentService
|
||||
protected DBTraceModuleManager moduleManager;
|
||||
@DependentService
|
||||
protected DBTraceObjectManager objectManager;
|
||||
@DependentService
|
||||
protected DBTraceOverlaySpaceAdapter overlaySpaceAdapter;
|
||||
@DependentService
|
||||
protected DBTraceReferenceManager referenceManager;
|
||||
|
@ -340,6 +343,14 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
|
|||
baseLanguage, this));
|
||||
}
|
||||
|
||||
@DependentService
|
||||
protected DBTraceObjectManager createObjectManager()
|
||||
throws CancelledException, IOException {
|
||||
return createTraceManager("Object Manager",
|
||||
(openMode, monitor) -> new DBTraceObjectManager(dbh, openMode, rwLock, monitor,
|
||||
baseLanguage, this));
|
||||
}
|
||||
|
||||
@DependentService
|
||||
protected DBTraceOverlaySpaceAdapter createOverlaySpaceAdapter()
|
||||
throws CancelledException, IOException {
|
||||
|
@ -391,9 +402,11 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
|
|||
}
|
||||
|
||||
@DependentService
|
||||
protected DBTraceThreadManager createThreadManager() throws IOException, CancelledException {
|
||||
protected DBTraceThreadManager createThreadManager(DBTraceObjectManager objectManager)
|
||||
throws IOException, CancelledException {
|
||||
return createTraceManager("Thread Manager",
|
||||
(openMode, monitor) -> new DBTraceThreadManager(dbh, openMode, rwLock, monitor, this));
|
||||
(openMode, monitor) -> new DBTraceThreadManager(dbh, openMode, rwLock, monitor, this,
|
||||
objectManager));
|
||||
}
|
||||
|
||||
@DependentService
|
||||
|
@ -494,6 +507,11 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
|
|||
return moduleManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBTraceObjectManager getObjectManager() {
|
||||
return objectManager;
|
||||
}
|
||||
|
||||
@Internal
|
||||
public DBTraceOverlaySpaceAdapter getOverlaySpaceAdapter() {
|
||||
return overlaySpaceAdapter;
|
||||
|
@ -716,29 +734,29 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
|
|||
}
|
||||
}
|
||||
|
||||
public void updateViewsAddRegionBlock(DBTraceMemoryRegion region) {
|
||||
public void updateViewsAddRegionBlock(TraceMemoryRegion region) {
|
||||
allViews(v -> v.updateMemoryAddRegionBlock(region));
|
||||
}
|
||||
|
||||
public void updateViewsChangeRegionBlockName(DBTraceMemoryRegion region) {
|
||||
public void updateViewsChangeRegionBlockName(TraceMemoryRegion region) {
|
||||
allViews(v -> v.updateMemoryChangeRegionBlockName(region));
|
||||
}
|
||||
|
||||
public void updateViewsChangeRegionBlockFlags(DBTraceMemoryRegion region) {
|
||||
allViews(v -> v.updateMemoryChangeRegionBlockFlags(region));
|
||||
public void updateViewsChangeRegionBlockFlags(TraceMemoryRegion region, Range<Long> lifespan) {
|
||||
allViews(v -> v.updateMemoryChangeRegionBlockFlags(region, lifespan));
|
||||
}
|
||||
|
||||
public void updateViewsChangeRegionBlockRange(DBTraceMemoryRegion region,
|
||||
public void updateViewsChangeRegionBlockRange(TraceMemoryRegion region,
|
||||
AddressRange oldRange, AddressRange newRange) {
|
||||
allViews(v -> v.updateMemoryChangeRegionBlockRange(region, oldRange, newRange));
|
||||
}
|
||||
|
||||
public void updateViewsChangeRegionBlockLifespan(DBTraceMemoryRegion region,
|
||||
public void updateViewsChangeRegionBlockLifespan(TraceMemoryRegion region,
|
||||
Range<Long> oldLifespan, Range<Long> newLifespan) {
|
||||
allViews(v -> v.updateMemoryChangeRegionBlockLifespan(region, oldLifespan, newLifespan));
|
||||
}
|
||||
|
||||
public void updateViewsDeleteRegionBlock(DBTraceMemoryRegion region) {
|
||||
public void updateViewsDeleteRegionBlock(TraceMemoryRegion region) {
|
||||
allViews(v -> v.updateMemoryDeleteRegionBlock(region));
|
||||
}
|
||||
|
||||
|
|
|
@ -19,13 +19,13 @@ import java.lang.reflect.Field;
|
|||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Iterator;
|
||||
import java.util.Objects;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.common.collect.BoundType;
|
||||
import com.google.common.collect.Range;
|
||||
import com.google.common.collect.*;
|
||||
|
||||
import db.*;
|
||||
import ghidra.program.model.address.*;
|
||||
|
@ -267,6 +267,155 @@ public enum DBTraceUtils {
|
|||
}
|
||||
}
|
||||
|
||||
public static abstract class RangeMapSetter<E, D extends Comparable<D>, R, V> {
|
||||
protected abstract R getRange(E entry);
|
||||
|
||||
protected abstract V getValue(E entry);
|
||||
|
||||
protected abstract void remove(E entry);
|
||||
|
||||
protected abstract D getLower(R range);
|
||||
|
||||
protected abstract D getUpper(R range);
|
||||
|
||||
protected abstract R toRange(D lower, D upper);
|
||||
|
||||
protected abstract D getPrevious(D d);
|
||||
|
||||
protected abstract D getNext(D d);
|
||||
|
||||
protected abstract Iterable<E> getIntersecting(D lower, D upper);
|
||||
|
||||
protected abstract E put(R range, V value);
|
||||
|
||||
protected D getPreviousOrSame(D d) {
|
||||
D prev = getPrevious(d);
|
||||
if (prev == null) {
|
||||
return d;
|
||||
}
|
||||
return prev;
|
||||
}
|
||||
|
||||
protected D getNextOrSame(D d) {
|
||||
D next = getNext(d);
|
||||
if (next == null) {
|
||||
return d;
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
protected boolean connects(R r1, R r2) {
|
||||
return getPreviousOrSame(getLower(r1)).compareTo(getUpper(r2)) <= 0 ||
|
||||
getPreviousOrSame(getLower(r2)).compareTo(getUpper(r1)) <= 0;
|
||||
}
|
||||
|
||||
public E set(R range, V value) {
|
||||
return set(getLower(range), getUpper(range), value);
|
||||
}
|
||||
|
||||
public E set(D lower, D upper, V value) {
|
||||
// Go one out to find abutting ranges, too.
|
||||
D prev = getPreviousOrSame(lower);
|
||||
D next = getNextOrSame(upper);
|
||||
Map<R, V> toPut = new HashMap<>();
|
||||
for (E entry : getIntersecting(prev, next)) {
|
||||
R r = getRange(entry);
|
||||
boolean precedesMin = getLower(r).compareTo(lower) < 0;
|
||||
boolean succeedsMax = getUpper(r).compareTo(upper) > 0;
|
||||
boolean sameVal = Objects.equals(getValue(entry), value);
|
||||
if (precedesMin && succeedsMax && sameVal) {
|
||||
return entry; // The value in this range is already set as specified
|
||||
}
|
||||
remove(entry);
|
||||
if (precedesMin) {
|
||||
if (sameVal) {
|
||||
lower = getLower(r);
|
||||
}
|
||||
else {
|
||||
toPut.put(toRange(getLower(r), prev), getValue(entry));
|
||||
}
|
||||
}
|
||||
if (succeedsMax) {
|
||||
if (sameVal) {
|
||||
upper = getUpper(r);
|
||||
}
|
||||
else {
|
||||
toPut.put(toRange(next, getUpper(r)), getValue(entry));
|
||||
}
|
||||
}
|
||||
}
|
||||
E result = put(toRange(lower, upper), value);
|
||||
assert toPut.size() <= 2;
|
||||
for (Entry<R, V> ent : toPut.entrySet()) {
|
||||
put(ent.getKey(), ent.getValue());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public static abstract class AddressRangeMapSetter<E, V>
|
||||
extends RangeMapSetter<E, Address, AddressRange, V> {
|
||||
@Override
|
||||
protected Address getLower(AddressRange range) {
|
||||
return range.getMinAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Address getUpper(AddressRange range) {
|
||||
return range.getMaxAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AddressRange toRange(Address lower, Address upper) {
|
||||
return new AddressRangeImpl(lower, upper);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Address getPrevious(Address d) {
|
||||
return d.previous();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Address getNext(Address d) {
|
||||
return d.next();
|
||||
}
|
||||
}
|
||||
|
||||
public static abstract class LifespanMapSetter<E, V>
|
||||
extends RangeMapSetter<E, Long, Range<Long>, V> {
|
||||
|
||||
@Override
|
||||
protected Long getLower(Range<Long> range) {
|
||||
return lowerEndpoint(range);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Long getUpper(Range<Long> range) {
|
||||
return upperEndpoint(range);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Range<Long> toRange(Long lower, Long upper) {
|
||||
return DBTraceUtils.toRange(lower, upper);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Long getPrevious(Long d) {
|
||||
if (d == null || d == Long.MIN_VALUE) {
|
||||
return null;
|
||||
}
|
||||
return d - 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Long getNext(Long d) {
|
||||
if (d == null || d == Long.MAX_VALUE) {
|
||||
return null;
|
||||
}
|
||||
return d + 1;
|
||||
}
|
||||
}
|
||||
|
||||
public static long lowerEndpoint(Range<Long> range) {
|
||||
if (!range.hasLowerBound()) {
|
||||
return Long.MIN_VALUE;
|
||||
|
@ -386,6 +535,16 @@ public enum DBTraceUtils {
|
|||
lifespanSetter.accept(data, toRange(data.getY1(), lowerEndpoint(span) - 1));
|
||||
}
|
||||
|
||||
public static List<Range<Long>> subtract(Range<Long> a, Range<Long> b) {
|
||||
RangeSet<Long> set = TreeRangeSet.create();
|
||||
set.add(a);
|
||||
set.remove(b);
|
||||
return set.asRanges()
|
||||
.stream()
|
||||
.map(r -> toRange(lowerEndpoint(r), upperEndpoint(r)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Iterator<T> covariantIterator(Iterator<? extends T> it) {
|
||||
// Iterators only support read and remove, not insert. Safe to cast.
|
||||
|
|
|
@ -63,11 +63,7 @@ public class DBTraceOverlaySpaceAdapter implements DBTraceManager {
|
|||
extends AbstractDBFieldCodec<Address, OT, BinaryField> {
|
||||
static final Charset UTF8 = Charset.forName("UTF-8");
|
||||
|
||||
public AddressDBFieldCodec(Class<OT> objectType, Field field, int column) {
|
||||
super(Address.class, objectType, BinaryField.class, field, column);
|
||||
}
|
||||
|
||||
protected byte[] encode(Address address) {
|
||||
public static byte[] encode(Address address) {
|
||||
if (address == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -86,6 +82,31 @@ public class DBTraceOverlaySpaceAdapter implements DBTraceManager {
|
|||
return buf.array();
|
||||
}
|
||||
|
||||
public static Address decode(byte[] enc, DBTraceOverlaySpaceAdapter osa) {
|
||||
if (enc == null) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
ByteBuffer buf = ByteBuffer.wrap(enc);
|
||||
byte overlay = buf.get();
|
||||
final AddressSpace as;
|
||||
if (overlay == 1) {
|
||||
short key = buf.getShort();
|
||||
as = osa.spacesByKey.get(key & 0xffffL);
|
||||
}
|
||||
else {
|
||||
short id = buf.getShort();
|
||||
as = osa.trace.getInternalAddressFactory().getAddressSpace(id);
|
||||
}
|
||||
long offset = buf.getLong();
|
||||
return as.getAddress(offset);
|
||||
}
|
||||
}
|
||||
|
||||
public AddressDBFieldCodec(Class<OT> objectType, Field field, int column) {
|
||||
super(Address.class, objectType, BinaryField.class, field, column);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void store(Address value, BinaryField f) {
|
||||
f.setBinaryData(encode(value));
|
||||
|
@ -101,25 +122,7 @@ public class DBTraceOverlaySpaceAdapter implements DBTraceManager {
|
|||
protected void doLoad(OT obj, DBRecord record)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
byte[] data = record.getBinaryData(column);
|
||||
if (data == null) {
|
||||
setValue(obj, null);
|
||||
}
|
||||
else {
|
||||
ByteBuffer buf = ByteBuffer.wrap(data);
|
||||
byte overlay = buf.get();
|
||||
final AddressSpace as;
|
||||
if (overlay == 1) {
|
||||
short key = buf.getShort();
|
||||
as = obj.getOverlaySpaceAdapter().spacesByKey.get(key & 0xffffL);
|
||||
}
|
||||
else {
|
||||
short id = buf.getShort();
|
||||
as = obj.getOverlaySpaceAdapter().trace.getInternalAddressFactory()
|
||||
.getAddressSpace(id);
|
||||
}
|
||||
long offset = buf.getLong();
|
||||
setValue(obj, as.getAddress(offset));
|
||||
}
|
||||
setValue(obj, decode(data, obj.getOverlaySpaceAdapter()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,10 +25,10 @@ import ghidra.trace.database.DBTrace;
|
|||
import ghidra.trace.database.DBTraceUtils;
|
||||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree;
|
||||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.AbstractDBTraceAddressSnapRangePropertyMapData;
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.model.Trace.TraceBookmarkChangeType;
|
||||
import ghidra.trace.model.bookmark.TraceBookmark;
|
||||
import ghidra.trace.model.bookmark.TraceBookmarkType;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.util.TraceChangeRecord;
|
||||
import ghidra.util.LockHold;
|
||||
import ghidra.util.database.DBCachedObjectStore;
|
||||
|
@ -99,7 +99,7 @@ public class DBTraceBookmark extends AbstractDBTraceAddressSnapRangePropertyMapD
|
|||
}
|
||||
|
||||
@Override
|
||||
public DBTraceThread getThread() {
|
||||
public TraceThread getThread() {
|
||||
return space.getThread();
|
||||
}
|
||||
|
||||
|
|
|
@ -31,7 +31,6 @@ import ghidra.program.model.lang.Language;
|
|||
import ghidra.trace.database.DBTrace;
|
||||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.TraceAddressSnapRangeQuery;
|
||||
import ghidra.trace.database.space.*;
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.database.thread.DBTraceThreadManager;
|
||||
import ghidra.trace.model.Trace.TraceBookmarkChangeType;
|
||||
import ghidra.trace.model.bookmark.TraceBookmarkManager;
|
||||
|
@ -160,7 +159,7 @@ public class DBTraceBookmarkManager
|
|||
protected static DBTraceSpaceKey unpackRegSpaceKey(AddressSpace space,
|
||||
DBTraceThreadManager threadManager, long id) {
|
||||
long threadKey = unpackRegThread(id);
|
||||
DBTraceThread thread = threadManager.getThread(threadKey);
|
||||
TraceThread thread = threadManager.getThread(threadKey);
|
||||
assert thread != null;
|
||||
int frameLevel = unpackRegFrame(id);
|
||||
return DBTraceSpaceKey.create(space, thread, frameLevel);
|
||||
|
@ -186,7 +185,7 @@ public class DBTraceBookmarkManager
|
|||
|
||||
@Override
|
||||
protected DBTraceBookmarkRegisterSpace createRegisterSpace(AddressSpace space,
|
||||
DBTraceThread thread, DBTraceSpaceEntry ent) throws VersionException, IOException {
|
||||
TraceThread thread, DBTraceSpaceEntry ent) throws VersionException, IOException {
|
||||
return new DBTraceBookmarkRegisterSpace(this, space, thread, ent.getFrameLevel());
|
||||
}
|
||||
|
||||
|
|
|
@ -18,24 +18,24 @@ package ghidra.trace.database.bookmark;
|
|||
import java.io.IOException;
|
||||
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.model.bookmark.TraceBookmarkRegisterSpace;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.util.exception.VersionException;
|
||||
|
||||
public class DBTraceBookmarkRegisterSpace extends DBTraceBookmarkSpace
|
||||
implements TraceBookmarkRegisterSpace {
|
||||
private final DBTraceThread thread;
|
||||
private final TraceThread thread;
|
||||
private final int frameLevel;
|
||||
|
||||
public DBTraceBookmarkRegisterSpace(DBTraceBookmarkManager manager, AddressSpace space,
|
||||
DBTraceThread thread, int frameLevel) throws VersionException, IOException {
|
||||
TraceThread thread, int frameLevel) throws VersionException, IOException {
|
||||
super(manager, space);
|
||||
this.thread = thread;
|
||||
this.frameLevel = frameLevel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBTraceThread getThread() {
|
||||
public TraceThread getThread() {
|
||||
return thread;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,10 +26,10 @@ import ghidra.trace.database.DBTrace;
|
|||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapSpace;
|
||||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.TraceAddressSnapRangeQuery;
|
||||
import ghidra.trace.database.space.DBTraceSpaceBased;
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.model.Trace.TraceBookmarkChangeType;
|
||||
import ghidra.trace.model.bookmark.TraceBookmarkSpace;
|
||||
import ghidra.trace.model.bookmark.TraceBookmarkType;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.util.TraceChangeRecord;
|
||||
import ghidra.util.LockHold;
|
||||
import ghidra.util.database.DBCachedObjectIndex;
|
||||
|
@ -65,7 +65,7 @@ public class DBTraceBookmarkSpace implements TraceBookmarkSpace, DBTraceSpaceBas
|
|||
}
|
||||
|
||||
@Override
|
||||
public DBTraceThread getThread() {
|
||||
public TraceThread getThread() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,6 @@ import ghidra.trace.database.DBTrace;
|
|||
import ghidra.trace.database.DBTraceUtils;
|
||||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree;
|
||||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.AbstractDBTraceAddressSnapRangePropertyMapData;
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.database.thread.DBTraceThreadManager;
|
||||
import ghidra.trace.model.Trace.TraceBreakpointChangeType;
|
||||
import ghidra.trace.model.breakpoint.TraceBreakpoint;
|
||||
|
@ -128,7 +127,7 @@ public class DBTraceBreakpoint
|
|||
return this;
|
||||
}
|
||||
|
||||
public void set(String path, String name, Collection<DBTraceThread> threads,
|
||||
public void set(String path, String name, Collection<TraceThread> threads,
|
||||
Collection<TraceBreakpointKind> kinds, boolean enabled, String comment) {
|
||||
// TODO: Check that the threads exist and that each's lifespan covers the breakpoint's
|
||||
// TODO: This would require additional validation any time those are updated
|
||||
|
@ -140,7 +139,7 @@ public class DBTraceBreakpoint
|
|||
}
|
||||
this.threadKeys = new long[threads.size()];
|
||||
int i = 0;
|
||||
for (DBTraceThread t : threads) {
|
||||
for (TraceThread t : threads) {
|
||||
this.threadKeys[i++] = t.getKey();
|
||||
}
|
||||
this.flagsByte = 0;
|
||||
|
@ -386,7 +385,8 @@ public class DBTraceBreakpoint
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
public boolean isEnabled(long snap) {
|
||||
// NB. Only object mode support per-snap enablement
|
||||
try (LockHold hold = LockHold.lock(space.lock.readLock())) {
|
||||
return enabled;
|
||||
}
|
||||
|
|
|
@ -30,7 +30,6 @@ import ghidra.trace.database.DBTrace;
|
|||
import ghidra.trace.database.DBTraceUtils;
|
||||
import ghidra.trace.database.space.AbstractDBTraceSpaceBasedManager;
|
||||
import ghidra.trace.database.space.DBTraceDelegatingManager;
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.database.thread.DBTraceThreadManager;
|
||||
import ghidra.trace.model.breakpoint.*;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
|
@ -60,7 +59,7 @@ public class DBTraceBreakpointManager
|
|||
}
|
||||
|
||||
@Override
|
||||
protected DBTraceBreakpointSpace createRegisterSpace(AddressSpace space, DBTraceThread thread,
|
||||
protected DBTraceBreakpointSpace createRegisterSpace(AddressSpace space, TraceThread thread,
|
||||
DBTraceSpaceEntry ent) throws VersionException, IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
@ -80,9 +79,9 @@ public class DBTraceBreakpointManager
|
|||
return lock.writeLock();
|
||||
}
|
||||
|
||||
protected void checkDuplicatePath(DBTraceBreakpoint ignore, String path, Range<Long> lifespan)
|
||||
protected void checkDuplicatePath(TraceBreakpoint ignore, String path, Range<Long> lifespan)
|
||||
throws DuplicateNameException {
|
||||
for (DBTraceBreakpoint pc : getBreakpointsByPath(path)) {
|
||||
for (TraceBreakpoint pc : getBreakpointsByPath(path)) {
|
||||
if (pc == ignore) {
|
||||
continue;
|
||||
}
|
||||
|
@ -98,23 +97,38 @@ public class DBTraceBreakpointManager
|
|||
public TraceBreakpoint addBreakpoint(String path, Range<Long> lifespan, AddressRange range,
|
||||
Collection<TraceThread> threads, Collection<TraceBreakpointKind> kinds, boolean enabled,
|
||||
String comment) throws DuplicateNameException {
|
||||
if (trace.getObjectManager().hasSchema()) {
|
||||
return trace.getObjectManager()
|
||||
.addBreakpoint(path, lifespan, range, threads, kinds, enabled, comment);
|
||||
}
|
||||
checkDuplicatePath(null, path, lifespan);
|
||||
return delegateWrite(range.getAddressSpace(),
|
||||
m -> m.addBreakpoint(path, lifespan, range, threads, kinds, enabled, comment));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends DBTraceBreakpoint> getAllBreakpoints() {
|
||||
public Collection<? extends TraceBreakpoint> getAllBreakpoints() {
|
||||
if (trace.getObjectManager().hasSchema()) {
|
||||
return trace.getObjectManager().getAllObjects(TraceObjectBreakpointLocation.class);
|
||||
}
|
||||
return delegateCollection(getActiveMemorySpaces(), m -> m.getAllBreakpoints());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends DBTraceBreakpoint> getBreakpointsByPath(String path) {
|
||||
public Collection<? extends TraceBreakpoint> getBreakpointsByPath(String path) {
|
||||
if (trace.getObjectManager().hasSchema()) {
|
||||
return trace.getObjectManager()
|
||||
.getObjectsByPath(path, TraceObjectBreakpointLocation.class);
|
||||
}
|
||||
return delegateCollection(getActiveMemorySpaces(), m -> m.getBreakpointsByPath(path));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceBreakpoint getPlacedBreakpointByPath(long snap, String path) {
|
||||
if (trace.getObjectManager().hasSchema()) {
|
||||
return trace.getObjectManager()
|
||||
.getObjectByPath(snap, path, TraceObjectBreakpointLocation.class);
|
||||
}
|
||||
try (LockHold hold = LockHold.lock(lock.readLock())) {
|
||||
return getBreakpointsByPath(path)
|
||||
.stream()
|
||||
|
@ -125,14 +139,24 @@ public class DBTraceBreakpointManager
|
|||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends DBTraceBreakpoint> getBreakpointsAt(long snap, Address address) {
|
||||
public Collection<? extends TraceBreakpoint> getBreakpointsAt(long snap, Address address) {
|
||||
if (trace.getObjectManager().hasSchema()) {
|
||||
return trace.getObjectManager()
|
||||
.getObjectsContaining(snap, address, TraceObjectBreakpointLocation.KEY_RANGE,
|
||||
TraceObjectBreakpointLocation.class);
|
||||
}
|
||||
return delegateRead(address.getAddressSpace(), m -> m.getBreakpointsAt(snap, address),
|
||||
Collections.emptyList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends DBTraceBreakpoint> getBreakpointsIntersecting(Range<Long> span,
|
||||
public Collection<? extends TraceBreakpoint> getBreakpointsIntersecting(Range<Long> span,
|
||||
AddressRange range) {
|
||||
if (trace.getObjectManager().hasSchema()) {
|
||||
return trace.getObjectManager()
|
||||
.getObjectsIntersecting(span, range, TraceObjectBreakpointLocation.KEY_RANGE,
|
||||
TraceObjectBreakpointLocation.class);
|
||||
}
|
||||
return delegateRead(range.getAddressSpace(), m -> m.getBreakpointsIntersecting(span, range),
|
||||
Collections.emptyList());
|
||||
}
|
||||
|
|
|
@ -99,11 +99,9 @@ public class DBTraceBreakpointSpace implements DBTraceSpaceBased {
|
|||
for (TraceThread t : threads) {
|
||||
threadManager.assertIsMine(t);
|
||||
}
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" }) // checked by above assertIsMine
|
||||
Collection<DBTraceThread> dbThreads = (Collection) threads;
|
||||
DBTraceBreakpoint breakpoint =
|
||||
breakpointMapSpace.put(new ImmutableTraceAddressSnapRange(range, lifespan), null);
|
||||
breakpoint.set(path, path, dbThreads, kinds, enabled, comment);
|
||||
breakpoint.set(path, path, threads, kinds, enabled, comment);
|
||||
trace.setChanged(
|
||||
new TraceChangeRecord<>(TraceBreakpointChangeType.ADDED, this, breakpoint));
|
||||
return breakpoint;
|
||||
|
|
|
@ -0,0 +1,285 @@
|
|||
/* ###
|
||||
* 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.trace.database.breakpoint;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.dbg.target.schema.TargetObjectSchema;
|
||||
import ghidra.dbg.util.PathMatcher;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.trace.database.DBTraceUtils;
|
||||
import ghidra.trace.database.target.*;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.Trace.TraceBreakpointChangeType;
|
||||
import ghidra.trace.model.breakpoint.*;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.trace.model.target.annot.TraceObjectInterfaceUtils;
|
||||
import ghidra.trace.model.thread.TraceObjectThread;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.util.*;
|
||||
import ghidra.util.LockHold;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
public class DBTraceObjectBreakpointLocation
|
||||
implements TraceObjectBreakpointLocation, DBTraceObjectInterface {
|
||||
|
||||
protected class BreakpointChangeTranslator extends Translator<TraceBreakpoint> {
|
||||
protected BreakpointChangeTranslator(DBTraceObject object, TraceBreakpoint iface) {
|
||||
super(KEY_RANGE, object, iface);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TraceChangeType<TraceBreakpoint, Void> getAddedType() {
|
||||
return TraceBreakpointChangeType.ADDED;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TraceChangeType<TraceBreakpoint, Range<Long>> getLifespanChangedType() {
|
||||
return TraceBreakpointChangeType.LIFESPAN_CHANGED;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TraceChangeType<TraceBreakpoint, Void> getChangedType() {
|
||||
return TraceBreakpointChangeType.CHANGED;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean appliesToKey(String key) {
|
||||
return KEY_RANGE.equals(key) ||
|
||||
TargetObject.DISPLAY_ATTRIBUTE_NAME.equals(key) ||
|
||||
TargetBreakpointSpec.ENABLED_ATTRIBUTE_NAME.equals(key) ||
|
||||
KEY_COMMENT.equals(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TraceChangeType<TraceBreakpoint, Void> getDeletedType() {
|
||||
return TraceBreakpointChangeType.DELETED;
|
||||
}
|
||||
}
|
||||
|
||||
private final DBTraceObject object;
|
||||
private final BreakpointChangeTranslator translator;
|
||||
|
||||
// Keep copies here for when the object gets invalidated
|
||||
private AddressRange range;
|
||||
|
||||
public DBTraceObjectBreakpointLocation(DBTraceObject object) {
|
||||
this.object = object;
|
||||
|
||||
translator = new BreakpointChangeTranslator(object, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Trace getTrace() {
|
||||
return object.getTrace();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
return object.getCanonicalPath().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(String name) {
|
||||
object.setValue(getLifespan(), TargetObject.DISPLAY_ATTRIBUTE_NAME, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return TraceObjectInterfaceUtils.getValue(object, getPlacedSnap(),
|
||||
TargetObject.DISPLAY_ATTRIBUTE_NAME, String.class, "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRange(AddressRange range) {
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
object.setValue(getLifespan(), KEY_RANGE, range);
|
||||
this.range = range;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRange getRange() {
|
||||
return range = TraceObjectInterfaceUtils.getValue(object, getPlacedSnap(), KEY_RANGE,
|
||||
AddressRange.class, range);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address getMinAddress() {
|
||||
AddressRange range = getRange();
|
||||
return range == null ? null : range.getMinAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address getMaxAddress() {
|
||||
AddressRange range = getRange();
|
||||
return range == null ? null : range.getMaxAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLength() {
|
||||
AddressRange range = getRange();
|
||||
return range == null ? 0 : range.getLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLifespan(Range<Long> lifespan) throws DuplicateNameException {
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
TraceObjectInterfaceUtils.setLifespan(TraceObjectBreakpointLocation.class, object,
|
||||
lifespan);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Range<Long> getLifespan() {
|
||||
return object.getLifespan();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getPlacedSnap() {
|
||||
return object.getMinSnap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClearedSnap(long clearedSnap) throws DuplicateNameException {
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
setLifespan(DBTraceUtils.toRange(getPlacedSnap(), clearedSnap));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getClearedSnap() {
|
||||
return object.getMaxSnap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceBreakpoint splitAndSet(long snap, boolean enabled,
|
||||
Collection<TraceBreakpointKind> kinds) {
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
if (enabled != isEnabled(snap)) {
|
||||
object.setValue(DBTraceUtils.toRange(snap, getClearedSnap()),
|
||||
TargetBreakpointSpec.ENABLED_ATTRIBUTE_NAME, enabled);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
object.setValue(getLifespan(), TargetBreakpointSpec.ENABLED_ATTRIBUTE_NAME, enabled);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled(long snap) {
|
||||
try (LockHold hold = object.getTrace().lockRead()) {
|
||||
Boolean locEn = TraceObjectInterfaceUtils.getValue(object, snap,
|
||||
TargetBreakpointSpec.ENABLED_ATTRIBUTE_NAME, Boolean.class, null);
|
||||
if (locEn != null) {
|
||||
return locEn;
|
||||
}
|
||||
return getSpecification().isEnabled(snap);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setKinds(Collection<TraceBreakpointKind> kinds) {
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
TraceObjectBreakpointSpec spec = getSpecification();
|
||||
if (spec.getObject() != this.getObject()) {
|
||||
throw new UnsupportedOperationException("Set via the specification instead");
|
||||
}
|
||||
spec.setKinds(kinds);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<TraceBreakpointKind> getKinds() {
|
||||
try (LockHold hold = object.getTrace().lockRead()) {
|
||||
return getSpecification().getKinds();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<TraceThread> getThreads() {
|
||||
// TODO: Delete this? It's sort of deprecated out the gate anyway....
|
||||
DBTraceObjectManager manager = object.getManager();
|
||||
TargetObjectSchema schema = manager.getRootSchema();
|
||||
try (LockHold hold = object.getTrace().lockRead()) {
|
||||
Set<TraceThread> threads =
|
||||
object.queryAncestorsInterface(getLifespan(), TraceObjectThread.class)
|
||||
.collect(Collectors.toSet());
|
||||
if (!threads.isEmpty()) {
|
||||
return threads;
|
||||
}
|
||||
|
||||
PathMatcher procMatcher = schema.searchFor(TargetProcess.class, false);
|
||||
return object.getAncestors(getLifespan(), procMatcher)
|
||||
.flatMap(proc -> proc.getFirstParent(object)
|
||||
.querySuccessorsInterface(getLifespan(),
|
||||
TraceObjectThread.class))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setComment(String comment) {
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
object.setValue(getLifespan(), KEY_COMMENT, comment);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getComment() {
|
||||
return TraceObjectInterfaceUtils.getValue(object, getPlacedSnap(), KEY_COMMENT,
|
||||
String.class, "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
object.deleteTree();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceObject getObject() {
|
||||
return object;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceObjectBreakpointSpec getSpecification() {
|
||||
try (LockHold hold = object.getTrace().lockRead()) {
|
||||
return object.queryAncestorsInterface(getLifespan(), TraceObjectBreakpointSpec.class)
|
||||
.findAny()
|
||||
.orElseThrow();
|
||||
}
|
||||
}
|
||||
|
||||
public TraceAddressSpace getTraceAddressSpace() {
|
||||
return spaceForValue(object.getMinSnap(), KEY_RANGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceChangeRecord<?, ?> translateEvent(TraceChangeRecord<?, ?> rec) {
|
||||
return translator.translate(rec);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,231 @@
|
|||
/* ###
|
||||
* 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.trace.database.breakpoint;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import ghidra.dbg.target.TargetBreakpointSpec;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.trace.database.DBTraceUtils;
|
||||
import ghidra.trace.database.target.DBTraceObject;
|
||||
import ghidra.trace.database.target.DBTraceObjectInterface;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.Trace.TraceBreakpointChangeType;
|
||||
import ghidra.trace.model.Trace.TraceObjectChangeType;
|
||||
import ghidra.trace.model.breakpoint.*;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.trace.model.target.TraceObjectValue;
|
||||
import ghidra.trace.model.target.annot.TraceObjectInterfaceUtils;
|
||||
import ghidra.trace.model.thread.TraceObjectThread;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.util.TraceAddressSpace;
|
||||
import ghidra.trace.util.TraceChangeRecord;
|
||||
import ghidra.util.LockHold;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
public class DBTraceObjectBreakpointSpec
|
||||
implements TraceObjectBreakpointSpec, DBTraceObjectInterface {
|
||||
private final DBTraceObject object;
|
||||
|
||||
private Set<TraceBreakpointKind> kinds;
|
||||
|
||||
public DBTraceObjectBreakpointSpec(DBTraceObject object) {
|
||||
this.object = object;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Trace getTrace() {
|
||||
return object.getTrace();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
return object.getCanonicalPath().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(String name) {
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
object.setValue(getLifespan(), TargetObject.DISPLAY_ATTRIBUTE_NAME, name);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return TraceObjectInterfaceUtils.getValue(object, getPlacedSnap(),
|
||||
TargetObject.DISPLAY_ATTRIBUTE_NAME, String.class, "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRange getRange() {
|
||||
throw new UnsupportedOperationException("Ask a location instead");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address getMinAddress() {
|
||||
throw new UnsupportedOperationException("Ask a location instead");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address getMaxAddress() {
|
||||
throw new UnsupportedOperationException("Ask a location instead");
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLength() {
|
||||
throw new UnsupportedOperationException("Ask a location instead");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Range<Long> getLifespan() {
|
||||
return object.getLifespan();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getPlacedSnap() {
|
||||
return object.getMinSnap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClearedSnap(long clearedSnap) throws DuplicateNameException {
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
setLifespan(DBTraceUtils.toRange(getPlacedSnap(), clearedSnap));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getClearedSnap() {
|
||||
return object.getMaxSnap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLifespan(Range<Long> lifespan) throws DuplicateNameException {
|
||||
TraceObjectInterfaceUtils.setLifespan(TraceObjectThread.class, object, lifespan);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceBreakpoint splitAndSet(long snap, boolean enabled,
|
||||
Collection<TraceBreakpointKind> kinds) {
|
||||
throw new UnsupportedOperationException("Only used by default trace recorder");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
object.setValue(getLifespan(), TargetBreakpointSpec.ENABLED_ATTRIBUTE_NAME, enabled);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled(long snap) {
|
||||
return TraceObjectInterfaceUtils.getValue(object, snap,
|
||||
TargetBreakpointSpec.ENABLED_ATTRIBUTE_NAME, Boolean.class, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setKinds(Collection<TraceBreakpointKind> kinds) {
|
||||
// TODO: More efficient encoding
|
||||
// TODO: Target-Trace mapping is implied by encoded name. Seems bad.
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
object.setValue(getLifespan(), TargetBreakpointSpec.KINDS_ATTRIBUTE_NAME,
|
||||
kinds.stream().map(k -> k.name()).collect(Collectors.joining(",")));
|
||||
this.kinds = Set.copyOf(kinds);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<TraceBreakpointKind> getKinds() {
|
||||
Set<TraceBreakpointKind> result = new HashSet<>();
|
||||
String kindsStr = TraceObjectInterfaceUtils.getValue(object, getPlacedSnap(),
|
||||
TargetBreakpointSpec.KINDS_ATTRIBUTE_NAME, String.class, null);
|
||||
if (kindsStr == null) {
|
||||
return kinds;
|
||||
}
|
||||
for (String name : kindsStr.split(",")) {
|
||||
try {
|
||||
result.add(TraceBreakpointKind.valueOf(name));
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
Msg.warn(this, "Could not decode breakpoint kind from trace database: " + name);
|
||||
}
|
||||
}
|
||||
return kinds = result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<TraceThread> getThreads() {
|
||||
throw new UnsupportedOperationException("Ask a location instead");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setComment(String comment) {
|
||||
throw new UnsupportedOperationException("Set on a location instead");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getComment() {
|
||||
throw new UnsupportedOperationException("Ask a location instead");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
object.deleteTree();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceObject getObject() {
|
||||
return object;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends TraceObjectBreakpointLocation> getLocations() {
|
||||
try (LockHold hold = object.getTrace().lockRead()) {
|
||||
return object
|
||||
.querySuccessorsInterface(getLifespan(), TraceObjectBreakpointLocation.class)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceChangeRecord<?, ?> translateEvent(TraceChangeRecord<?, ?> rec) {
|
||||
if (rec.getEventType() == TraceObjectChangeType.VALUE_CHANGED.getType()) {
|
||||
TraceChangeRecord<TraceObjectValue, Object> cast =
|
||||
TraceObjectChangeType.VALUE_CHANGED.cast(rec);
|
||||
String key = cast.getAffectedObject().getEntryKey();
|
||||
boolean applies = TargetBreakpointSpec.KINDS_ATTRIBUTE_NAME.equals(key) ||
|
||||
TargetBreakpointSpec.ENABLED_ATTRIBUTE_NAME.equals(key);
|
||||
if (!applies) {
|
||||
return null;
|
||||
}
|
||||
assert cast.getAffectedObject().getParent() == object;
|
||||
for (TraceObjectBreakpointLocation loc : getLocations()) {
|
||||
DBTraceObjectBreakpointLocation dbLoc = (DBTraceObjectBreakpointLocation) loc;
|
||||
TraceAddressSpace space = dbLoc.getTraceAddressSpace();
|
||||
TraceChangeRecord<?, ?> evt = new TraceChangeRecord<>(
|
||||
TraceBreakpointChangeType.CHANGED, space, loc, null, null);
|
||||
object.getTrace().setChanged(evt);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -36,7 +36,6 @@ import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree;
|
|||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.AbstractDBTraceAddressSnapRangePropertyMapData;
|
||||
import ghidra.trace.database.space.AbstractDBTraceSpaceBasedManager;
|
||||
import ghidra.trace.database.space.DBTraceDelegatingManager;
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.database.thread.DBTraceThreadManager;
|
||||
import ghidra.trace.model.TraceAddressSnapRange;
|
||||
import ghidra.trace.model.context.TraceRegisterContextManager;
|
||||
|
@ -106,7 +105,7 @@ public class DBTraceRegisterContextManager extends
|
|||
|
||||
@Override
|
||||
protected DBTraceRegisterContextRegisterSpace createRegisterSpace(AddressSpace space,
|
||||
DBTraceThread thread, DBTraceSpaceEntry ent) throws VersionException, IOException {
|
||||
TraceThread thread, DBTraceSpaceEntry ent) throws VersionException, IOException {
|
||||
// TODO: Should I just forbid this? It doesn't seem sane. Then again, what do I know?
|
||||
return new DBTraceRegisterContextRegisterSpace(this, dbh, space, ent, thread);
|
||||
}
|
||||
|
|
|
@ -20,17 +20,17 @@ import java.io.IOException;
|
|||
import db.DBHandle;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.trace.database.space.AbstractDBTraceSpaceBasedManager.DBTraceSpaceEntry;
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.model.context.TraceRegisterContextRegisterSpace;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.util.exception.VersionException;
|
||||
|
||||
public class DBTraceRegisterContextRegisterSpace extends DBTraceRegisterContextSpace
|
||||
implements TraceRegisterContextRegisterSpace {
|
||||
private final DBTraceThread thread;
|
||||
private final TraceThread thread;
|
||||
private final int frameLevel;
|
||||
|
||||
public DBTraceRegisterContextRegisterSpace(DBTraceRegisterContextManager manager, DBHandle dbh,
|
||||
AddressSpace space, DBTraceSpaceEntry ent, DBTraceThread thread)
|
||||
AddressSpace space, DBTraceSpaceEntry ent, TraceThread thread)
|
||||
throws VersionException, IOException {
|
||||
super(manager, dbh, space, ent);
|
||||
this.thread = thread;
|
||||
|
@ -38,7 +38,7 @@ public class DBTraceRegisterContextRegisterSpace extends DBTraceRegisterContextS
|
|||
}
|
||||
|
||||
@Override
|
||||
public DBTraceThread getThread() {
|
||||
public TraceThread getThread() {
|
||||
return thread;
|
||||
}
|
||||
|
||||
|
|
|
@ -37,10 +37,10 @@ import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapSpace;
|
|||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.TraceAddressSnapRangeQuery;
|
||||
import ghidra.trace.database.space.AbstractDBTraceSpaceBasedManager.DBTraceSpaceEntry;
|
||||
import ghidra.trace.database.space.DBTraceSpaceBased;
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.model.ImmutableTraceAddressSnapRange;
|
||||
import ghidra.trace.model.TraceAddressSnapRange;
|
||||
import ghidra.trace.model.context.TraceRegisterContextSpace;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.util.LockHold;
|
||||
import ghidra.util.database.*;
|
||||
import ghidra.util.database.annot.*;
|
||||
|
@ -126,12 +126,12 @@ public class DBTraceRegisterContextSpace implements TraceRegisterContextSpace, D
|
|||
}
|
||||
|
||||
@Override
|
||||
public DBTraceThread getThread() {
|
||||
public TraceThread getThread() {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected long getThreadKey() {
|
||||
DBTraceThread thread = getThread();
|
||||
TraceThread thread = getThread();
|
||||
return thread == null ? -1 : thread.getKey();
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,6 @@ import ghidra.trace.database.DBTraceUtils;
|
|||
import ghidra.trace.database.data.DBTraceDataSettingsAdapter.DBTraceSettingsEntry;
|
||||
import ghidra.trace.database.map.*;
|
||||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.AbstractDBTraceAddressSnapRangePropertyMapData;
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.database.thread.DBTraceThreadManager;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.util.TraceAddressSpace;
|
||||
|
@ -183,7 +182,7 @@ public class DBTraceDataSettingsAdapter
|
|||
implements DBTraceDataSettingsOperations {
|
||||
public DBTraceDataSettingsRegisterSpace(String tableName,
|
||||
DBCachedObjectStoreFactory storeFactory, ReadWriteLock lock, AddressSpace space,
|
||||
DBTraceThread thread, int frameLevel, Class<DBTraceSettingsEntry> dataType,
|
||||
TraceThread thread, int frameLevel, Class<DBTraceSettingsEntry> dataType,
|
||||
DBTraceAddressSnapRangePropertyMapDataFactory<DBTraceSettingsEntry, DBTraceSettingsEntry> dataFactory)
|
||||
throws VersionException, IOException {
|
||||
super(tableName, storeFactory, lock, space, thread, frameLevel, dataType, dataFactory);
|
||||
|
@ -217,7 +216,7 @@ public class DBTraceDataSettingsAdapter
|
|||
|
||||
@Override
|
||||
protected DBTraceAddressSnapRangePropertyMapRegisterSpace<DBTraceSettingsEntry, DBTraceSettingsEntry> createRegisterSpace(
|
||||
AddressSpace space, DBTraceThread thread, DBTraceSpaceEntry ent)
|
||||
AddressSpace space, TraceThread thread, DBTraceSpaceEntry ent)
|
||||
throws VersionException, IOException {
|
||||
return new DBTraceDataSettingsRegisterSpace(
|
||||
tableName(space, ent.getThreadKey(), ent.getFrameLevel()),
|
||||
|
|
|
@ -46,7 +46,6 @@ import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.TraceAdd
|
|||
import ghidra.trace.database.space.AbstractDBTraceSpaceBasedManager;
|
||||
import ghidra.trace.database.space.DBTraceDelegatingManager;
|
||||
import ghidra.trace.database.symbol.DBTraceReferenceManager;
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.database.thread.DBTraceThreadManager;
|
||||
import ghidra.trace.model.AddressSnap;
|
||||
import ghidra.trace.model.DefaultAddressSnap;
|
||||
|
@ -222,7 +221,7 @@ public class DBTraceCodeManager
|
|||
|
||||
// Internal
|
||||
public UndefinedDBTraceData doCreateUndefinedUnit(long snap, Address address,
|
||||
DBTraceThread thread, int frameLevel) {
|
||||
TraceThread thread, int frameLevel) {
|
||||
return undefinedCache.computeIfAbsent(new DefaultAddressSnap(address, snap),
|
||||
ot -> new UndefinedDBTraceData(trace, snap, address, thread, frameLevel));
|
||||
}
|
||||
|
@ -279,7 +278,7 @@ public class DBTraceCodeManager
|
|||
}
|
||||
|
||||
@Override
|
||||
protected DBTraceCodeRegisterSpace createRegisterSpace(AddressSpace space, DBTraceThread thread,
|
||||
protected DBTraceCodeRegisterSpace createRegisterSpace(AddressSpace space, TraceThread thread,
|
||||
DBTraceSpaceEntry ent) throws VersionException, IOException {
|
||||
return new DBTraceCodeRegisterSpace(this, dbh, space, ent, thread);
|
||||
}
|
||||
|
|
|
@ -20,23 +20,23 @@ import java.io.IOException;
|
|||
import db.DBHandle;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.trace.database.space.AbstractDBTraceSpaceBasedManager.DBTraceSpaceEntry;
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.model.listing.TraceCodeRegisterSpace;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.util.exception.VersionException;
|
||||
|
||||
public class DBTraceCodeRegisterSpace extends DBTraceCodeSpace implements TraceCodeRegisterSpace {
|
||||
protected final DBTraceThread thread;
|
||||
protected final TraceThread thread;
|
||||
private final int frameLevel;
|
||||
|
||||
public DBTraceCodeRegisterSpace(DBTraceCodeManager manager, DBHandle dbh, AddressSpace space,
|
||||
DBTraceSpaceEntry ent, DBTraceThread thread) throws VersionException, IOException {
|
||||
DBTraceSpaceEntry ent, TraceThread thread) throws VersionException, IOException {
|
||||
super(manager, dbh, space, ent);
|
||||
this.thread = thread;
|
||||
this.frameLevel = ent.getFrameLevel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBTraceThread getThread() {
|
||||
public TraceThread getThread() {
|
||||
return thread;
|
||||
}
|
||||
|
||||
|
|
|
@ -37,9 +37,9 @@ import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.TraceAdd
|
|||
import ghidra.trace.database.space.AbstractDBTraceSpaceBasedManager.DBTraceSpaceEntry;
|
||||
import ghidra.trace.database.space.DBTraceSpaceBased;
|
||||
import ghidra.trace.database.symbol.DBTraceReferenceManager;
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.model.TraceAddressSnapRange;
|
||||
import ghidra.trace.model.listing.TraceCodeSpace;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.util.ByteArrayUtils;
|
||||
import ghidra.util.LockHold;
|
||||
import ghidra.util.database.DBCachedObjectStoreFactory;
|
||||
|
@ -169,7 +169,7 @@ public class DBTraceCodeSpace implements TraceCodeSpace, DBTraceSpaceBased {
|
|||
}
|
||||
|
||||
@Override
|
||||
public DBTraceThread getThread() {
|
||||
public TraceThread getThread() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -32,10 +32,10 @@ import ghidra.program.model.mem.Memory;
|
|||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.trace.database.DBTrace;
|
||||
import ghidra.trace.database.memory.DBTraceMemoryRegion;
|
||||
import ghidra.trace.database.symbol.DBTraceReference;
|
||||
import ghidra.trace.model.listing.TraceCodeUnit;
|
||||
import ghidra.trace.model.map.TracePropertyMap;
|
||||
import ghidra.trace.model.memory.TraceMemoryRegion;
|
||||
import ghidra.trace.model.program.TraceProgramView;
|
||||
import ghidra.trace.model.symbol.TraceReference;
|
||||
import ghidra.trace.model.symbol.TraceSymbol;
|
||||
|
@ -74,7 +74,7 @@ public interface DBTraceCodeUnitAdapter extends TraceCodeUnit, MemBufferAdapter
|
|||
if (!showBlockName) {
|
||||
return address.toString(false, pad);
|
||||
}
|
||||
DBTraceMemoryRegion region =
|
||||
TraceMemoryRegion region =
|
||||
getTrace().getMemoryManager().getRegionContaining(getStartSnap(), address);
|
||||
if (region == null) {
|
||||
return address.toString(showBlockName, pad);
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
*/
|
||||
package ghidra.trace.database.listing;
|
||||
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.model.listing.TraceCodeUnitsRegisterView;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
|
||||
public class DBTraceCodeUnitsRegisterView extends DBTraceCodeUnitsView
|
||||
implements TraceCodeUnitsRegisterView {
|
||||
|
@ -25,7 +25,7 @@ public class DBTraceCodeUnitsRegisterView extends DBTraceCodeUnitsView
|
|||
}
|
||||
|
||||
@Override
|
||||
public DBTraceThread getThread() {
|
||||
public TraceThread getThread() {
|
||||
return space.getThread();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
*/
|
||||
package ghidra.trace.database.listing;
|
||||
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.model.listing.TraceDataRegisterView;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
|
||||
public class DBTraceDataRegisterView extends DBTraceDataView implements TraceDataRegisterView {
|
||||
public DBTraceDataRegisterView(DBTraceCodeSpace space) {
|
||||
|
@ -24,7 +24,7 @@ public class DBTraceDataRegisterView extends DBTraceDataView implements TraceDat
|
|||
}
|
||||
|
||||
@Override
|
||||
public DBTraceThread getThread() {
|
||||
public TraceThread getThread() {
|
||||
return space.getThread();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
*/
|
||||
package ghidra.trace.database.listing;
|
||||
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.model.listing.TraceInstructionsRegisterView;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
|
||||
public class DBTraceInstructionsRegisterView extends DBTraceInstructionsView
|
||||
implements TraceInstructionsRegisterView {
|
||||
|
@ -26,7 +26,7 @@ public class DBTraceInstructionsRegisterView extends DBTraceInstructionsView
|
|||
}
|
||||
|
||||
@Override
|
||||
public DBTraceThread getThread() {
|
||||
public TraceThread getThread() {
|
||||
return space.getThread();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,6 @@ import ghidra.trace.model.listing.TraceInstructionsView;
|
|||
import ghidra.trace.util.OverlappingObjectIterator;
|
||||
import ghidra.trace.util.TraceChangeRecord;
|
||||
import ghidra.util.LockHold;
|
||||
import ghidra.util.SystemUtilities;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
|
|
|
@ -31,10 +31,10 @@ import ghidra.trace.database.DBTraceUtils;
|
|||
import ghidra.trace.database.data.DBTraceDataSettingsOperations;
|
||||
import ghidra.trace.database.memory.DBTraceMemorySpace;
|
||||
import ghidra.trace.database.space.DBTraceSpaceKey;
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.model.ImmutableTraceAddressSnapRange;
|
||||
import ghidra.trace.model.TraceAddressSnapRange;
|
||||
import ghidra.trace.model.listing.TraceData;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.util.TraceAddressSpace;
|
||||
|
||||
public class UndefinedDBTraceData implements DBTraceDataAdapter, DBTraceSpaceKey {
|
||||
|
@ -42,10 +42,10 @@ public class UndefinedDBTraceData implements DBTraceDataAdapter, DBTraceSpaceKey
|
|||
protected final long snap;
|
||||
protected final Range<Long> lifespan;
|
||||
protected final Address address;
|
||||
protected final DBTraceThread thread;
|
||||
protected final TraceThread thread;
|
||||
protected final int frameLevel;
|
||||
|
||||
public UndefinedDBTraceData(DBTrace trace, long snap, Address address, DBTraceThread thread,
|
||||
public UndefinedDBTraceData(DBTrace trace, long snap, Address address, TraceThread thread,
|
||||
int frameLevel) {
|
||||
this.trace = trace;
|
||||
this.snap = snap;
|
||||
|
@ -118,7 +118,7 @@ public class UndefinedDBTraceData implements DBTraceDataAdapter, DBTraceSpaceKey
|
|||
}
|
||||
|
||||
@Override
|
||||
public DBTraceThread getThread() {
|
||||
public TraceThread getThread() {
|
||||
return thread;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,6 @@ import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.Abstract
|
|||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.TraceAddressSnapRangeQuery;
|
||||
import ghidra.trace.database.space.AbstractDBTraceSpaceBasedManager;
|
||||
import ghidra.trace.database.space.DBTraceDelegatingManager;
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.database.thread.DBTraceThreadManager;
|
||||
import ghidra.trace.model.TraceAddressSnapRange;
|
||||
import ghidra.trace.model.map.TraceAddressSnapRangePropertyMap;
|
||||
|
@ -89,7 +88,7 @@ public class DBTraceAddressSnapRangePropertyMap<T, DR extends AbstractDBTraceAdd
|
|||
|
||||
@Override
|
||||
protected DBTraceAddressSnapRangePropertyMapRegisterSpace<T, DR> createRegisterSpace(
|
||||
AddressSpace space, DBTraceThread thread, DBTraceSpaceEntry ent)
|
||||
AddressSpace space, TraceThread thread, DBTraceSpaceEntry ent)
|
||||
throws VersionException, IOException {
|
||||
return new DBTraceAddressSnapRangePropertyMapRegisterSpace<>(
|
||||
tableName(space, ent.getThreadKey(), ent.getFrameLevel()), trace.getStoreFactory(),
|
||||
|
|
|
@ -21,20 +21,20 @@ import java.util.concurrent.locks.ReadWriteLock;
|
|||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMap.DBTraceAddressSnapRangePropertyMapDataFactory;
|
||||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.AbstractDBTraceAddressSnapRangePropertyMapData;
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.model.map.TraceAddressSnapRangePropertyMapRegisterSpace;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.util.database.DBCachedObjectStoreFactory;
|
||||
import ghidra.util.exception.VersionException;
|
||||
|
||||
public class DBTraceAddressSnapRangePropertyMapRegisterSpace<T, DR extends AbstractDBTraceAddressSnapRangePropertyMapData<T>>
|
||||
extends DBTraceAddressSnapRangePropertyMapSpace<T, DR>
|
||||
implements TraceAddressSnapRangePropertyMapRegisterSpace<T> {
|
||||
protected final DBTraceThread thread;
|
||||
protected final TraceThread thread;
|
||||
protected final int frameLevel;
|
||||
|
||||
public DBTraceAddressSnapRangePropertyMapRegisterSpace(String tableName,
|
||||
DBCachedObjectStoreFactory storeFactory, ReadWriteLock lock, AddressSpace space,
|
||||
DBTraceThread thread, int frameLevel, Class<DR> dataType,
|
||||
TraceThread thread, int frameLevel, Class<DR> dataType,
|
||||
DBTraceAddressSnapRangePropertyMapDataFactory<T, DR> dataFactory)
|
||||
throws VersionException, IOException {
|
||||
super(tableName, storeFactory, lock, space, dataType, dataFactory);
|
||||
|
@ -43,7 +43,7 @@ public class DBTraceAddressSnapRangePropertyMapRegisterSpace<T, DR extends Abstr
|
|||
}
|
||||
|
||||
@Override
|
||||
public DBTraceThread getThread() {
|
||||
public TraceThread getThread() {
|
||||
return thread;
|
||||
}
|
||||
|
||||
|
|
|
@ -29,9 +29,9 @@ import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMap.DBTraceAddre
|
|||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.AbstractDBTraceAddressSnapRangePropertyMapData;
|
||||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.TraceAddressSnapRangeQuery;
|
||||
import ghidra.trace.database.space.DBTraceSpaceBased;
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.model.TraceAddressSnapRange;
|
||||
import ghidra.trace.model.map.TraceAddressSnapRangePropertyMapSpace;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.util.LockHold;
|
||||
import ghidra.util.database.*;
|
||||
import ghidra.util.database.spatial.AbstractConstraintsTreeSpatialMap;
|
||||
|
@ -69,7 +69,7 @@ public class DBTraceAddressSnapRangePropertyMapSpace<T, DR extends AbstractDBTra
|
|||
}
|
||||
|
||||
@Override
|
||||
public DBTraceThread getThread() {
|
||||
public TraceThread getThread() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ import com.google.common.collect.Collections2;
|
|||
import com.google.common.collect.Range;
|
||||
|
||||
import db.DBHandle;
|
||||
import ghidra.dbg.target.TargetMemoryRegion;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.program.model.mem.MemBuffer;
|
||||
|
@ -36,7 +37,6 @@ import ghidra.trace.database.address.DBTraceOverlaySpaceAdapter;
|
|||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.TraceAddressSnapRangeQuery;
|
||||
import ghidra.trace.database.space.AbstractDBTraceSpaceBasedManager;
|
||||
import ghidra.trace.database.space.DBTraceDelegatingManager;
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.database.thread.DBTraceThreadManager;
|
||||
import ghidra.trace.model.TraceAddressSnapRange;
|
||||
import ghidra.trace.model.memory.*;
|
||||
|
@ -85,7 +85,7 @@ public class DBTraceMemoryManager
|
|||
|
||||
@Override
|
||||
protected DBTraceMemoryRegisterSpace createRegisterSpace(AddressSpace space,
|
||||
DBTraceThread thread, DBTraceSpaceEntry ent) throws VersionException, IOException {
|
||||
TraceThread thread, DBTraceSpaceEntry ent) throws VersionException, IOException {
|
||||
return new DBTraceMemoryRegisterSpace(this, dbh, space, ent, thread);
|
||||
}
|
||||
|
||||
|
@ -128,9 +128,12 @@ public class DBTraceMemoryManager
|
|||
}
|
||||
|
||||
@Override
|
||||
public DBTraceMemoryRegion addRegion(String path, Range<Long> lifespan,
|
||||
public TraceMemoryRegion addRegion(String path, Range<Long> lifespan,
|
||||
AddressRange range, Collection<TraceMemoryFlag> flags)
|
||||
throws TraceOverlappedRegionException, DuplicateNameException {
|
||||
if (trace.getObjectManager().hasSchema()) {
|
||||
return trace.getObjectManager().addMemoryRegion(path, lifespan, range, flags);
|
||||
}
|
||||
try {
|
||||
return delegateWrite(range.getAddressSpace(),
|
||||
m -> m.addRegion(path, lifespan, range, flags));
|
||||
|
@ -144,35 +147,50 @@ public class DBTraceMemoryManager
|
|||
}
|
||||
|
||||
@Override
|
||||
public Collection<TraceMemoryRegion> getAllRegions() {
|
||||
public Collection<? extends TraceMemoryRegion> getAllRegions() {
|
||||
if (trace.getObjectManager().hasSchema()) {
|
||||
return trace.getObjectManager().getAllObjects(TraceObjectMemoryRegion.class);
|
||||
}
|
||||
return delegateCollection(getActiveMemorySpaces(), m -> m.getAllRegions());
|
||||
}
|
||||
|
||||
// Internal
|
||||
public Collection<DBTraceMemoryRegion> getRegionsInternal() {
|
||||
return delegateCollection(getActiveMemorySpaces(), m -> m.regionMapSpace.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBTraceMemoryRegion getLiveRegionByPath(long snap, String regionName) {
|
||||
public TraceMemoryRegion getLiveRegionByPath(long snap, String path) {
|
||||
if (trace.getObjectManager().hasSchema()) {
|
||||
return trace.getObjectManager()
|
||||
.getObjectByPath(snap, path, TraceObjectMemoryRegion.class);
|
||||
}
|
||||
// Not efficient, but I don't anticipate many regions
|
||||
return delegateFirst(getActiveMemorySpaces(), m -> m.getLiveRegionByPath(snap, regionName));
|
||||
return delegateFirst(getActiveMemorySpaces(), m -> m.getLiveRegionByPath(snap, path));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBTraceMemoryRegion getRegionContaining(long snap, Address address) {
|
||||
public TraceMemoryRegion getRegionContaining(long snap, Address address) {
|
||||
if (trace.getObjectManager().hasSchema()) {
|
||||
return trace.getObjectManager()
|
||||
.getObjectContaining(snap, address, TargetMemoryRegion.RANGE_ATTRIBUTE_NAME,
|
||||
TraceObjectMemoryRegion.class);
|
||||
}
|
||||
return delegateRead(address.getAddressSpace(), m -> m.getRegionContaining(snap, address));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends DBTraceMemoryRegion> getRegionsIntersecting(Range<Long> lifespan,
|
||||
public Collection<? extends TraceMemoryRegion> getRegionsIntersecting(Range<Long> lifespan,
|
||||
AddressRange range) {
|
||||
if (trace.getObjectManager().hasSchema()) {
|
||||
return trace.getObjectManager()
|
||||
.getObjectsIntersecting(lifespan, range,
|
||||
TargetMemoryRegion.RANGE_ATTRIBUTE_NAME, TraceObjectMemoryRegion.class);
|
||||
}
|
||||
return delegateRead(range.getAddressSpace(), m -> m.getRegionsIntersecting(lifespan, range),
|
||||
Collections.emptyList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends DBTraceMemoryRegion> getRegionsAtSnap(long snap) {
|
||||
public Collection<? extends TraceMemoryRegion> getRegionsAtSnap(long snap) {
|
||||
if (trace.getObjectManager().hasSchema()) {
|
||||
return trace.getObjectManager().getObjectsAtSnap(snap, TraceObjectMemoryRegion.class);
|
||||
}
|
||||
return delegateCollection(memSpaces.values(), m -> m.getRegionsAtSnap(snap));
|
||||
}
|
||||
|
||||
|
@ -193,6 +211,11 @@ public class DBTraceMemoryManager
|
|||
|
||||
@Override
|
||||
public AddressSetView getRegionsAddressSet(long snap) {
|
||||
if (trace.getObjectManager().hasSchema()) {
|
||||
return trace.getObjectManager()
|
||||
.getObjectsAddressSet(snap, TargetMemoryRegion.RANGE_ATTRIBUTE_NAME,
|
||||
TraceObjectMemoryRegion.class, r -> true);
|
||||
}
|
||||
return new UnionAddressSetView(Collections2.transform(getActiveMemorySpaces(),
|
||||
m -> m.getRegionsAddressSet(snap)));
|
||||
}
|
||||
|
@ -200,6 +223,11 @@ public class DBTraceMemoryManager
|
|||
@Override
|
||||
public AddressSetView getRegionsAddressSetWith(long snap,
|
||||
Predicate<TraceMemoryRegion> predicate) {
|
||||
if (trace.getObjectManager().hasSchema()) {
|
||||
return trace.getObjectManager()
|
||||
.getObjectsAddressSet(snap, TargetMemoryRegion.RANGE_ATTRIBUTE_NAME,
|
||||
TraceObjectMemoryRegion.class, predicate);
|
||||
}
|
||||
return new UnionAddressSetView(Collections2.transform(getActiveMemorySpaces(),
|
||||
m -> m.getRegionsAddressSetWith(snap, predicate)));
|
||||
}
|
||||
|
|
|
@ -65,7 +65,7 @@ public class DBTraceMemoryRegion
|
|||
|
||||
private final DBTraceMemorySpace space;
|
||||
|
||||
private final Set<TraceMemoryFlag> flags = EnumSet.noneOf(TraceMemoryFlag.class);
|
||||
private final EnumSet<TraceMemoryFlag> flags = EnumSet.noneOf(TraceMemoryFlag.class);
|
||||
|
||||
public DBTraceMemoryRegion(DBTraceMemorySpace space,
|
||||
DBTraceAddressSnapRangePropertyMapTree<DBTraceMemoryRegion, DBTraceMemoryRegion> tree,
|
||||
|
@ -81,11 +81,7 @@ public class DBTraceMemoryRegion
|
|||
return;
|
||||
}
|
||||
flags.clear();
|
||||
for (TraceMemoryFlag f : TraceMemoryFlag.values()) {
|
||||
if ((flagsByte & f.getBits()) != 0) {
|
||||
flags.add(f);
|
||||
}
|
||||
}
|
||||
TraceMemoryFlag.fromBits(flags, flagsByte);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -271,14 +267,11 @@ public class DBTraceMemoryRegion
|
|||
@Override
|
||||
public void setFlags(Collection<TraceMemoryFlag> flags) {
|
||||
try (LockHold hold = LockHold.lock(space.lock.writeLock())) {
|
||||
this.flagsByte = 0;
|
||||
this.flagsByte = TraceMemoryFlag.toBits(flags);
|
||||
this.flags.clear();
|
||||
for (TraceMemoryFlag f : flags) {
|
||||
this.flagsByte |= f.getBits();
|
||||
this.flags.add(f);
|
||||
}
|
||||
this.flags.addAll(flags);
|
||||
update(FLAGS_COLUMN);
|
||||
space.trace.updateViewsChangeRegionBlockFlags(this);
|
||||
space.trace.updateViewsChangeRegionBlockFlags(this, lifespan);
|
||||
}
|
||||
space.trace.setChanged(
|
||||
new TraceChangeRecord<>(TraceMemoryRegionChangeType.CHANGED, space, this));
|
||||
|
@ -288,12 +281,10 @@ public class DBTraceMemoryRegion
|
|||
@Override
|
||||
public void addFlags(Collection<TraceMemoryFlag> flags) {
|
||||
try (LockHold hold = LockHold.lock(space.lock.writeLock())) {
|
||||
for (TraceMemoryFlag f : flags) {
|
||||
this.flagsByte |= f.getBits();
|
||||
this.flags.add(f);
|
||||
}
|
||||
this.flagsByte |= TraceMemoryFlag.toBits(flags);
|
||||
this.flags.addAll(flags);
|
||||
update(FLAGS_COLUMN);
|
||||
space.trace.updateViewsChangeRegionBlockFlags(this);
|
||||
space.trace.updateViewsChangeRegionBlockFlags(this, lifespan);
|
||||
}
|
||||
space.trace.setChanged(
|
||||
new TraceChangeRecord<>(TraceMemoryRegionChangeType.CHANGED, space, this));
|
||||
|
@ -303,12 +294,10 @@ public class DBTraceMemoryRegion
|
|||
@Override
|
||||
public void clearFlags(Collection<TraceMemoryFlag> flags) {
|
||||
try (LockHold hold = LockHold.lock(space.lock.writeLock())) {
|
||||
for (TraceMemoryFlag f : flags) {
|
||||
this.flagsByte &= ~f.getBits();
|
||||
this.flags.remove(f);
|
||||
}
|
||||
this.flagsByte &= ~TraceMemoryFlag.toBits(flags);
|
||||
this.flags.removeAll(flags);
|
||||
update(FLAGS_COLUMN);
|
||||
space.trace.updateViewsChangeRegionBlockFlags(this);
|
||||
space.trace.updateViewsChangeRegionBlockFlags(this, lifespan);
|
||||
}
|
||||
space.trace.setChanged(
|
||||
new TraceChangeRecord<>(TraceMemoryRegionChangeType.CHANGED, space, this));
|
||||
|
|
|
@ -26,18 +26,18 @@ import ghidra.program.model.address.AddressRange;
|
|||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.trace.database.listing.DBTraceCodeRegisterSpace;
|
||||
import ghidra.trace.database.space.AbstractDBTraceSpaceBasedManager.DBTraceSpaceEntry;
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.model.memory.*;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.util.exception.VersionException;
|
||||
|
||||
public class DBTraceMemoryRegisterSpace extends DBTraceMemorySpace
|
||||
implements TraceMemoryRegisterSpace {
|
||||
protected final DBTraceThread thread;
|
||||
protected final TraceThread thread;
|
||||
private final int frameLevel;
|
||||
|
||||
public DBTraceMemoryRegisterSpace(DBTraceMemoryManager manager, DBHandle dbh,
|
||||
AddressSpace space, DBTraceSpaceEntry ent, DBTraceThread thread)
|
||||
AddressSpace space, DBTraceSpaceEntry ent, TraceThread thread)
|
||||
throws IOException, VersionException {
|
||||
super(manager, dbh, space, ent);
|
||||
this.thread = thread;
|
||||
|
@ -45,7 +45,7 @@ public class DBTraceMemoryRegisterSpace extends DBTraceMemorySpace
|
|||
}
|
||||
|
||||
@Override
|
||||
public DBTraceThread getThread() {
|
||||
public TraceThread getThread() {
|
||||
return thread;
|
||||
}
|
||||
|
||||
|
|
|
@ -33,16 +33,17 @@ import ghidra.program.model.address.*;
|
|||
import ghidra.program.model.mem.MemBuffer;
|
||||
import ghidra.trace.database.DBTrace;
|
||||
import ghidra.trace.database.DBTraceUtils;
|
||||
import ghidra.trace.database.DBTraceUtils.AddressRangeMapSetter;
|
||||
import ghidra.trace.database.DBTraceUtils.OffsetSnap;
|
||||
import ghidra.trace.database.listing.DBTraceCodeSpace;
|
||||
import ghidra.trace.database.map.*;
|
||||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.TraceAddressSnapRangeQuery;
|
||||
import ghidra.trace.database.space.AbstractDBTraceSpaceBasedManager.DBTraceSpaceEntry;
|
||||
import ghidra.trace.database.space.DBTraceSpaceBased;
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.model.*;
|
||||
import ghidra.trace.model.Trace.*;
|
||||
import ghidra.trace.model.memory.*;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.util.TraceChangeRecord;
|
||||
import ghidra.trace.util.TraceViewportSpanIterator;
|
||||
import ghidra.util.*;
|
||||
|
@ -261,7 +262,7 @@ public class DBTraceMemorySpace implements Unfinished, TraceMemorySpace, DBTrace
|
|||
}
|
||||
|
||||
@Override
|
||||
public DBTraceThread getThread() {
|
||||
public TraceThread getThread() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -274,55 +275,42 @@ public class DBTraceMemorySpace implements Unfinished, TraceMemorySpace, DBTrace
|
|||
if (state == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
// Go one out to find abutting ranges, too.
|
||||
Address prev = start.previous();
|
||||
if (prev == null) {
|
||||
prev = start;
|
||||
}
|
||||
Address next = end.next();
|
||||
if (next == null) {
|
||||
next = end;
|
||||
}
|
||||
Map<TraceAddressSnapRange, TraceMemoryState> toPut = new HashMap<>();
|
||||
for (Entry<TraceAddressSnapRange, TraceMemoryState> entry : stateMapSpace.reduce(
|
||||
TraceAddressSnapRangeQuery.intersecting(prev, next, snap, snap)).entries()) {
|
||||
// NOTE: Entries are in no particular order
|
||||
AddressRange range = entry.getKey().getRange();
|
||||
boolean precedesMin = range.getMinAddress().compareTo(start) < 0;
|
||||
boolean procedesMax = range.getMaxAddress().compareTo(end) > 0;
|
||||
boolean sameState = entry.getValue() == state;
|
||||
if (precedesMin && procedesMax && sameState) {
|
||||
return; // The value in this range is already the desired state
|
||||
|
||||
new AddressRangeMapSetter<Entry<TraceAddressSnapRange, TraceMemoryState>, TraceMemoryState>() {
|
||||
@Override
|
||||
protected AddressRange getRange(Entry<TraceAddressSnapRange, TraceMemoryState> entry) {
|
||||
return entry.getKey().getRange();
|
||||
}
|
||||
stateMapSpace.remove(entry);
|
||||
if (precedesMin) {
|
||||
if (sameState) {
|
||||
start = range.getMinAddress();
|
||||
}
|
||||
else {
|
||||
toPut.put(
|
||||
new ImmutableTraceAddressSnapRange(range.getMinAddress(), prev, snap, snap),
|
||||
entry.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TraceMemoryState getValue(
|
||||
Entry<TraceAddressSnapRange, TraceMemoryState> entry) {
|
||||
return entry.getValue();
|
||||
}
|
||||
if (procedesMax) {
|
||||
if (sameState) {
|
||||
end = range.getMaxAddress();
|
||||
}
|
||||
else {
|
||||
toPut.put(
|
||||
new ImmutableTraceAddressSnapRange(next, range.getMaxAddress(), snap, snap),
|
||||
entry.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void remove(Entry<TraceAddressSnapRange, TraceMemoryState> entry) {
|
||||
stateMapSpace.remove(entry);
|
||||
}
|
||||
}
|
||||
if (state != TraceMemoryState.UNKNOWN) {
|
||||
stateMapSpace.put(start, end, snap, state);
|
||||
}
|
||||
assert toPut.size() <= 2;
|
||||
for (Entry<TraceAddressSnapRange, TraceMemoryState> ent : toPut.entrySet()) {
|
||||
stateMapSpace.put(ent.getKey(), ent.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Iterable<Entry<TraceAddressSnapRange, TraceMemoryState>> getIntersecting(
|
||||
Address lower, Address upper) {
|
||||
return stateMapSpace
|
||||
.reduce(TraceAddressSnapRangeQuery.intersecting(lower, upper, snap, snap))
|
||||
.entries();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Entry<TraceAddressSnapRange, TraceMemoryState> put(AddressRange range,
|
||||
TraceMemoryState value) {
|
||||
if (value != TraceMemoryState.UNKNOWN) {
|
||||
stateMapSpace.put(new ImmutableTraceAddressSnapRange(range, snap), value);
|
||||
}
|
||||
return null; // Don't need to return it
|
||||
}
|
||||
}.set(start, end, state);
|
||||
|
||||
trace.setChanged(new TraceChangeRecord<>(TraceMemoryStateChangeType.CHANGED, this,
|
||||
new ImmutableTraceAddressSnapRange(start, end, snap, snap), state));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,312 @@
|
|||
/* ###
|
||||
* 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.trace.database.memory;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import ghidra.dbg.target.TargetMemoryRegion;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.trace.database.DBTraceUtils;
|
||||
import ghidra.trace.database.target.DBTraceObject;
|
||||
import ghidra.trace.database.target.DBTraceObjectInterface;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.Trace.TraceMemoryRegionChangeType;
|
||||
import ghidra.trace.model.memory.*;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.trace.model.target.TraceObjectValue;
|
||||
import ghidra.trace.model.target.annot.TraceObjectInterfaceUtils;
|
||||
import ghidra.trace.util.TraceChangeRecord;
|
||||
import ghidra.trace.util.TraceChangeType;
|
||||
import ghidra.util.LockHold;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
public class DBTraceObjectMemoryRegion implements TraceObjectMemoryRegion, DBTraceObjectInterface {
|
||||
|
||||
protected class RegionChangeTranslator extends Translator<TraceMemoryRegion> {
|
||||
protected RegionChangeTranslator(DBTraceObject object, TraceMemoryRegion iface) {
|
||||
super(TargetMemoryRegion.RANGE_ATTRIBUTE_NAME, object, iface);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TraceChangeType<TraceMemoryRegion, Void> getAddedType() {
|
||||
return TraceMemoryRegionChangeType.ADDED;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TraceChangeType<TraceMemoryRegion, Range<Long>> getLifespanChangedType() {
|
||||
return TraceMemoryRegionChangeType.LIFESPAN_CHANGED;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TraceChangeType<TraceMemoryRegion, Void> getChangedType() {
|
||||
return TraceMemoryRegionChangeType.CHANGED;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean appliesToKey(String key) {
|
||||
return TargetMemoryRegion.RANGE_ATTRIBUTE_NAME.equals(key) ||
|
||||
TargetObject.DISPLAY_ATTRIBUTE_NAME.equals(key) ||
|
||||
TargetMemoryRegion.READABLE_ATTRIBUTE_NAME.equals(key) ||
|
||||
TargetMemoryRegion.WRITABLE_ATTRIBUTE_NAME.equals(key) ||
|
||||
TargetMemoryRegion.EXECUTABLE_ATTRIBUTE_NAME.equals(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TraceChangeType<TraceMemoryRegion, Void> getDeletedType() {
|
||||
return TraceMemoryRegionChangeType.DELETED;
|
||||
}
|
||||
}
|
||||
|
||||
private final DBTraceObject object;
|
||||
private final RegionChangeTranslator translator;
|
||||
|
||||
public DBTraceObjectMemoryRegion(DBTraceObject object) {
|
||||
this.object = object;
|
||||
|
||||
translator = new RegionChangeTranslator(object, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Trace getTrace() {
|
||||
return object.getTrace();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
return object.getCanonicalPath().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(String name) {
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
object.setValue(getLifespan(), TargetObject.DISPLAY_ATTRIBUTE_NAME, name);
|
||||
object.getTrace().updateViewsChangeRegionBlockName(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
TraceObjectValue value =
|
||||
object.getValue(getCreationSnap(), TargetObject.DISPLAY_ATTRIBUTE_NAME);
|
||||
return value == null ? "" : (String) value.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLifespan(Range<Long> newLifespan) throws DuplicateNameException {
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
Range<Long> oldLifespan = getLifespan();
|
||||
if (Objects.equals(oldLifespan, newLifespan)) {
|
||||
return;
|
||||
}
|
||||
TraceObjectInterfaceUtils.setLifespan(TraceObjectMemoryRegion.class, object,
|
||||
newLifespan);
|
||||
object.getTrace().updateViewsChangeRegionBlockLifespan(this, oldLifespan, newLifespan);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Range<Long> getLifespan() {
|
||||
return object.getLifespan();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCreationSnap(long creationSnap) throws DuplicateNameException {
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
setLifespan(DBTraceUtils.toRange(creationSnap, getDestructionSnap()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getCreationSnap() {
|
||||
return object.getMinSnap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDestructionSnap(long destructionSnap) throws DuplicateNameException {
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
setLifespan(DBTraceUtils.toRange(getCreationSnap(), destructionSnap));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDestructionSnap() {
|
||||
return object.getMaxSnap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRange(AddressRange newRange) {
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
AddressRange oldRange = getRange();
|
||||
if (Objects.equals(oldRange, newRange)) {
|
||||
return;
|
||||
}
|
||||
object.setValue(getLifespan(), TargetMemoryRegion.RANGE_ATTRIBUTE_NAME, newRange);
|
||||
object.getTrace().updateViewsChangeRegionBlockRange(this, oldRange, newRange);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRange getRange() {
|
||||
try (LockHold hold = object.getTrace().lockRead()) {
|
||||
return TraceObjectInterfaceUtils.getValue(object, getCreationSnap(),
|
||||
TargetMemoryRegion.RANGE_ATTRIBUTE_NAME, AddressRange.class, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMinAddress(Address min) {
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
setRange(DBTraceUtils.toRange(min, getMaxAddress()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address getMinAddress() {
|
||||
AddressRange range = getRange();
|
||||
return range == null ? null : range.getMinAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxAddress(Address max) {
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
setRange(DBTraceUtils.toRange(getMinAddress(), max));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address getMaxAddress() {
|
||||
AddressRange range = getRange();
|
||||
return range == null ? null : range.getMaxAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLength(long length) throws AddressOverflowException {
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
setRange(new AddressRangeImpl(getMinAddress(), length));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLength() {
|
||||
return getRange().getLength();
|
||||
}
|
||||
|
||||
protected static String keyForFlag(TraceMemoryFlag flag) {
|
||||
switch (flag) {
|
||||
case READ:
|
||||
return TargetMemoryRegion.READABLE_ATTRIBUTE_NAME;
|
||||
case WRITE:
|
||||
return TargetMemoryRegion.WRITABLE_ATTRIBUTE_NAME;
|
||||
case EXECUTE:
|
||||
return TargetMemoryRegion.EXECUTABLE_ATTRIBUTE_NAME;
|
||||
case VOLATILE:
|
||||
return KEY_VOLATILE;
|
||||
default:
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFlags(Range<Long> lifespan, Collection<TraceMemoryFlag> flags) {
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
for (TraceMemoryFlag flag : TraceMemoryFlag.values()) {
|
||||
Boolean val = flags.contains(flag) ? true : null;
|
||||
object.setValue(lifespan, keyForFlag(flag), val);
|
||||
}
|
||||
object.getTrace().updateViewsChangeRegionBlockFlags(this, lifespan);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addFlags(Range<Long> lifespan, Collection<TraceMemoryFlag> flags) {
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
for (TraceMemoryFlag flag : flags) {
|
||||
object.setValue(lifespan, keyForFlag(flag), true);
|
||||
}
|
||||
object.getTrace().updateViewsChangeRegionBlockFlags(this, lifespan);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearFlags(Range<Long> lifespan, Collection<TraceMemoryFlag> flags) {
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
for (TraceMemoryFlag flag : flags) {
|
||||
object.setValue(lifespan, keyForFlag(flag), null);
|
||||
}
|
||||
object.getTrace().updateViewsChangeRegionBlockFlags(this, lifespan);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFlags(Collection<TraceMemoryFlag> flags) {
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
setFlags(getLifespan(), flags);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addFlags(Collection<TraceMemoryFlag> flags) {
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
addFlags(getLifespan(), flags);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearFlags(Collection<TraceMemoryFlag> flags) {
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
clearFlags(getLifespan(), flags);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<TraceMemoryFlag> getFlags(long snap) {
|
||||
EnumSet<TraceMemoryFlag> result = EnumSet.noneOf(TraceMemoryFlag.class);
|
||||
for (TraceMemoryFlag flag : TraceMemoryFlag.values()) {
|
||||
if (object.getValue(snap, keyForFlag(flag)) != null) {
|
||||
result.add(flag);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<TraceMemoryFlag> getFlags() {
|
||||
try (LockHold hold = object.getTrace().lockRead()) {
|
||||
return getFlags(getCreationSnap());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
object.deleteTree();
|
||||
object.getTrace().updateViewsDeleteRegionBlock(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceObject getObject() {
|
||||
return object;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceChangeRecord<?, ?> translateEvent(TraceChangeRecord<?, ?> rec) {
|
||||
return translator.translate(rec);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
/* ###
|
||||
* 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.trace.database.memory;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import ghidra.dbg.target.TargetRegister;
|
||||
import ghidra.dbg.util.PathUtils;
|
||||
import ghidra.pcode.utils.Utils;
|
||||
import ghidra.trace.database.target.DBTraceObject;
|
||||
import ghidra.trace.model.memory.TraceMemoryState;
|
||||
import ghidra.trace.model.memory.TraceObjectRegister;
|
||||
import ghidra.trace.model.target.*;
|
||||
import ghidra.trace.model.target.annot.TraceObjectInterfaceUtils;
|
||||
import ghidra.trace.model.thread.TraceObjectThread;
|
||||
|
||||
public class DBTraceObjectRegister implements TraceObjectRegister {
|
||||
private final DBTraceObject object;
|
||||
|
||||
public DBTraceObjectRegister(DBTraceObject object) {
|
||||
this.object = object;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceObject getObject() {
|
||||
return object;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceObjectThread getThread() {
|
||||
return object.queryAncestorsInterface(object.getLifespan(), TraceObjectThread.class)
|
||||
.findAny()
|
||||
.orElseThrow();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
TraceObjectKeyPath path = object.getCanonicalPath();
|
||||
if (PathUtils.isIndex(path.key())) {
|
||||
return path.index();
|
||||
}
|
||||
return path.key();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLength() {
|
||||
return TraceObjectInterfaceUtils.getValue(object, object.getMinSnap(),
|
||||
TargetRegister.LENGTH_ATTRIBUTE_NAME, Integer.class, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(Range<Long> lifespan, byte[] value) {
|
||||
int length = getLength();
|
||||
if (length != 0 && value.length != length) {
|
||||
throw new IllegalArgumentException("Length must match the register");
|
||||
}
|
||||
object.setValue(lifespan, TargetRegister.VALUE_ATTRIBUTE_NAME, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getValue(long snap) {
|
||||
TraceObjectValue ov = object.getValue(snap, TargetRegister.VALUE_ATTRIBUTE_NAME);
|
||||
if (ov == null) {
|
||||
return null;
|
||||
}
|
||||
Object val = ov.getValue();
|
||||
if (val instanceof byte[]) {
|
||||
// TODO: Should I correct mismatched size?
|
||||
return (byte[]) val;
|
||||
}
|
||||
if (val instanceof String) {
|
||||
// Always base 16. Model API says byte array for register value is big endian.
|
||||
BigInteger bigVal = new BigInteger((String) val, 16);
|
||||
return Utils.bigIntegerToBytes(bigVal, getLength(), true);
|
||||
}
|
||||
throw new ClassCastException("Cannot convert " + val + " to byte array for register value");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setState(Range<Long> lifespan, TraceMemoryState state) {
|
||||
// NB. There's no model equivalent, so encode using ordinal
|
||||
object.setValue(lifespan, KEY_STATE, state.ordinal());
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceMemoryState getState(long snap) {
|
||||
return TraceMemoryState.values()[TraceObjectInterfaceUtils.getValue(object, snap, KEY_STATE,
|
||||
Integer.class, TraceMemoryState.UNKNOWN.ordinal())];
|
||||
}
|
||||
}
|
|
@ -23,16 +23,16 @@ import java.util.concurrent.locks.ReadWriteLock;
|
|||
import com.google.common.collect.Range;
|
||||
|
||||
import db.DBHandle;
|
||||
import ghidra.dbg.target.TargetModule;
|
||||
import ghidra.dbg.target.TargetSection;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.trace.database.DBTrace;
|
||||
import ghidra.trace.database.DBTraceUtils;
|
||||
import ghidra.trace.database.space.AbstractDBTraceSpaceBasedManager;
|
||||
import ghidra.trace.database.space.DBTraceDelegatingManager;
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.model.Trace.TraceModuleChangeType;
|
||||
import ghidra.trace.model.modules.TraceModuleManager;
|
||||
import ghidra.trace.model.modules.TraceSection;
|
||||
import ghidra.trace.model.modules.*;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.util.TraceChangeRecord;
|
||||
import ghidra.util.LockHold;
|
||||
|
@ -64,9 +64,9 @@ public class DBTraceModuleManager
|
|||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
protected void checkModulePathConflicts(DBTraceModule ignore, String modulePath,
|
||||
protected void checkModulePathConflicts(TraceModule ignore, String modulePath,
|
||||
Range<Long> moduleLifespan) throws DuplicateNameException {
|
||||
for (DBTraceModule pc : doGetModulesByPath(modulePath)) {
|
||||
for (TraceModule pc : doGetModulesByPath(modulePath)) {
|
||||
if (pc == ignore) {
|
||||
continue;
|
||||
}
|
||||
|
@ -80,12 +80,17 @@ public class DBTraceModuleManager
|
|||
|
||||
protected void checkSectionPathConflicts(DBTraceSection ignore, String sectionPath,
|
||||
Range<Long> moduleLifespan) throws DuplicateNameException {
|
||||
Collection<? extends DBTraceSection> pathConflicts = doGetSectionsByPath(sectionPath);
|
||||
for (DBTraceSection pc : pathConflicts) {
|
||||
Collection<? extends TraceSection> pathConflicts = doGetSectionsByPath(sectionPath);
|
||||
for (TraceSection pc : pathConflicts) {
|
||||
if (pc == ignore) {
|
||||
continue;
|
||||
}
|
||||
if (!DBTraceUtils.intersect(pc.getLifespan(), moduleLifespan)) {
|
||||
/**
|
||||
* TODO: Certainly, any two sections at the same path will belong to the same module and
|
||||
* so have the same lifespan, no? I suppose this logic is only true in objects mode...,
|
||||
* and there, the logic is performed by the value key duplicate check.
|
||||
*/
|
||||
if (!DBTraceUtils.intersect(pc.getModule().getLifespan(), moduleLifespan)) {
|
||||
continue;
|
||||
}
|
||||
throw new DuplicateNameException("Section with path '" + sectionPath +
|
||||
|
@ -94,31 +99,41 @@ public class DBTraceModuleManager
|
|||
}
|
||||
|
||||
@Override
|
||||
public DBTraceModule addModule(String modulePath, String moduleName, AddressRange range,
|
||||
public TraceModule addModule(String modulePath, String moduleName, AddressRange range,
|
||||
Range<Long> lifespan) throws DuplicateNameException {
|
||||
try (LockHold hold = LockHold.lock(lock.writeLock())) {
|
||||
return doAddModule(modulePath, moduleName, range, lifespan);
|
||||
}
|
||||
}
|
||||
|
||||
protected DBTraceModule doAddModule(String modulePath, String moduleName, AddressRange range,
|
||||
protected TraceModule doAddModule(String modulePath, String moduleName, AddressRange range,
|
||||
Range<Long> lifespan) throws DuplicateNameException {
|
||||
if (trace.getObjectManager().hasSchema()) {
|
||||
return trace.getObjectManager().addModule(modulePath, moduleName, lifespan, range);
|
||||
}
|
||||
checkModulePathConflicts(null, modulePath, lifespan);
|
||||
return delegateWrite(range.getAddressSpace(),
|
||||
m -> m.doAddModule(modulePath, moduleName, range, lifespan));
|
||||
}
|
||||
|
||||
protected Collection<? extends DBTraceModule> doGetModulesByPath(String modulePath) {
|
||||
protected Collection<? extends TraceModule> doGetModulesByPath(String modulePath) {
|
||||
if (trace.getObjectManager().hasSchema()) {
|
||||
return trace.getObjectManager().getObjectsByPath(modulePath, TraceObjectModule.class);
|
||||
}
|
||||
return delegateCollection(memSpaces.values(), m -> m.doGetModulesByPath(modulePath));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends DBTraceModule> getModulesByPath(String modulePath) {
|
||||
public Collection<? extends TraceModule> getModulesByPath(String modulePath) {
|
||||
return Collections.unmodifiableCollection(doGetModulesByPath(modulePath));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBTraceModule getLoadedModuleByPath(long snap, String modulePath) {
|
||||
public TraceModule getLoadedModuleByPath(long snap, String modulePath) {
|
||||
if (trace.getObjectManager().hasSchema()) {
|
||||
return trace.getObjectManager()
|
||||
.getObjectByPath(snap, modulePath, TraceObjectModule.class);
|
||||
}
|
||||
try (LockHold hold = LockHold.lock(lock.readLock())) {
|
||||
return doGetModulesByPath(modulePath)
|
||||
.stream()
|
||||
|
@ -129,24 +144,40 @@ public class DBTraceModuleManager
|
|||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends DBTraceModule> getAllModules() {
|
||||
public Collection<? extends TraceModule> getAllModules() {
|
||||
if (trace.getObjectManager().hasSchema()) {
|
||||
return trace.getObjectManager().getAllObjects(TraceObjectModule.class);
|
||||
}
|
||||
return delegateCollection(memSpaces.values(), m -> m.getAllModules());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends DBTraceModule> getLoadedModules(long snap) {
|
||||
public Collection<? extends TraceModule> getLoadedModules(long snap) {
|
||||
if (trace.getObjectManager().hasSchema()) {
|
||||
return trace.getObjectManager().getObjectsAtSnap(snap, TraceObjectModule.class);
|
||||
}
|
||||
return delegateCollection(memSpaces.values(), m -> m.getLoadedModules(snap));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends DBTraceModule> getModulesAt(long snap, Address address) {
|
||||
public Collection<? extends TraceModule> getModulesAt(long snap, Address address) {
|
||||
if (trace.getObjectManager().hasSchema()) {
|
||||
return trace.getObjectManager()
|
||||
.getObjectsContaining(snap, address, TargetModule.RANGE_ATTRIBUTE_NAME,
|
||||
TraceObjectModule.class);
|
||||
}
|
||||
return delegateRead(address.getAddressSpace(),
|
||||
m -> m.getModulesAt(snap, address), Set.of());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends DBTraceModule> getModulesIntersecting(Range<Long> lifespan,
|
||||
public Collection<? extends TraceModule> getModulesIntersecting(Range<Long> lifespan,
|
||||
AddressRange range) {
|
||||
if (trace.getObjectManager().hasSchema()) {
|
||||
return trace.getObjectManager()
|
||||
.getObjectsIntersecting(lifespan, range, TargetModule.RANGE_ATTRIBUTE_NAME,
|
||||
TraceObjectModule.class);
|
||||
}
|
||||
return delegateRead(range.getAddressSpace(),
|
||||
m -> m.getModulesIntersecting(lifespan, range), Set.of());
|
||||
}
|
||||
|
@ -157,14 +188,24 @@ public class DBTraceModuleManager
|
|||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends DBTraceSection> getSectionsAt(long snap, Address address) {
|
||||
public Collection<? extends TraceSection> getSectionsAt(long snap, Address address) {
|
||||
if (trace.getObjectManager().hasSchema()) {
|
||||
return trace.getObjectManager()
|
||||
.getObjectsContaining(snap, address, TargetSection.RANGE_ATTRIBUTE_NAME,
|
||||
TraceObjectSection.class);
|
||||
}
|
||||
return delegateRead(address.getAddressSpace(),
|
||||
m -> m.getSectionsAt(snap, address), Set.of());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends DBTraceSection> getSectionsIntersecting(Range<Long> lifespan,
|
||||
public Collection<? extends TraceSection> getSectionsIntersecting(Range<Long> lifespan,
|
||||
AddressRange range) {
|
||||
if (trace.getObjectManager().hasSchema()) {
|
||||
return trace.getObjectManager()
|
||||
.getObjectsIntersecting(lifespan, range, TargetSection.RANGE_ATTRIBUTE_NAME,
|
||||
TraceObjectSection.class);
|
||||
}
|
||||
return delegateRead(range.getAddressSpace(),
|
||||
m -> m.getSectionsIntersecting(lifespan, range), Set.of());
|
||||
}
|
||||
|
@ -186,7 +227,7 @@ public class DBTraceModuleManager
|
|||
}
|
||||
|
||||
@Override
|
||||
protected DBTraceModuleSpace createRegisterSpace(AddressSpace space, DBTraceThread thread,
|
||||
protected DBTraceModuleSpace createRegisterSpace(AddressSpace space, TraceThread thread,
|
||||
DBTraceSpaceEntry ent) throws VersionException, IOException {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
@ -204,7 +245,10 @@ public class DBTraceModuleManager
|
|||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends DBTraceSection> getAllSections() {
|
||||
public Collection<? extends TraceSection> getAllSections() {
|
||||
if (trace.getObjectManager().hasSchema()) {
|
||||
return trace.getObjectManager().getAllObjects(TraceObjectSection.class);
|
||||
}
|
||||
return delegateCollection(memSpaces.values(), m -> m.getAllSections());
|
||||
}
|
||||
|
||||
|
@ -212,12 +256,15 @@ public class DBTraceModuleManager
|
|||
return delegateCollection(memSpaces.values(), m -> m.doGetSectionsByModuleId(key));
|
||||
}
|
||||
|
||||
protected Collection<? extends DBTraceSection> doGetSectionsByPath(String sectionPath) {
|
||||
protected Collection<? extends TraceSection> doGetSectionsByPath(String sectionPath) {
|
||||
if (trace.getObjectManager().hasSchema()) {
|
||||
return trace.getObjectManager().getObjectsByPath(sectionPath, TraceObjectSection.class);
|
||||
}
|
||||
return delegateCollection(memSpaces.values(), m -> m.doGetSectionsByPath(sectionPath));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends DBTraceSection> getSectionsByPath(String sectionPath) {
|
||||
public Collection<? extends TraceSection> getSectionsByPath(String sectionPath) {
|
||||
try (LockHold hold = LockHold.lock(lock.readLock())) {
|
||||
return Collections.unmodifiableCollection(doGetSectionsByPath(sectionPath));
|
||||
}
|
||||
|
@ -225,10 +272,14 @@ public class DBTraceModuleManager
|
|||
|
||||
@Override
|
||||
public TraceSection getLoadedSectionByPath(long snap, String sectionPath) {
|
||||
if (trace.getObjectManager().hasSchema()) {
|
||||
return trace.getObjectManager()
|
||||
.getObjectByPath(snap, sectionPath, TraceObjectSection.class);
|
||||
}
|
||||
try (LockHold hold = LockHold.lock(lock.readLock())) {
|
||||
return doGetSectionsByPath(sectionPath)
|
||||
.stream()
|
||||
.filter(s -> s.getLifespan().contains(snap))
|
||||
.filter(s -> s.getModule().getLifespan().contains(snap))
|
||||
.findAny()
|
||||
.orElse(null);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,242 @@
|
|||
/* ###
|
||||
* 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.trace.database.module;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.dbg.util.PathMatcher;
|
||||
import ghidra.dbg.util.PathUtils;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.trace.database.DBTraceUtils;
|
||||
import ghidra.trace.database.target.*;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.Trace.TraceModuleChangeType;
|
||||
import ghidra.trace.model.modules.*;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.trace.model.target.annot.TraceObjectInterfaceUtils;
|
||||
import ghidra.trace.util.TraceChangeRecord;
|
||||
import ghidra.trace.util.TraceChangeType;
|
||||
import ghidra.util.LockHold;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
public class DBTraceObjectModule implements TraceObjectModule, DBTraceObjectInterface {
|
||||
|
||||
protected class ModuleChangeTranslator extends Translator<TraceModule> {
|
||||
protected ModuleChangeTranslator(DBTraceObject object, TraceModule iface) {
|
||||
super(TargetModule.RANGE_ATTRIBUTE_NAME, object, iface);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TraceChangeType<TraceModule, Void> getAddedType() {
|
||||
return TraceModuleChangeType.ADDED;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TraceChangeType<TraceModule, Range<Long>> getLifespanChangedType() {
|
||||
return TraceModuleChangeType.LIFESPAN_CHANGED;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TraceChangeType<TraceModule, Void> getChangedType() {
|
||||
return TraceModuleChangeType.CHANGED;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean appliesToKey(String key) {
|
||||
return TargetModule.RANGE_ATTRIBUTE_NAME.equals(key) ||
|
||||
TargetObject.DISPLAY_ATTRIBUTE_NAME.equals(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TraceChangeType<TraceModule, Void> getDeletedType() {
|
||||
return TraceModuleChangeType.DELETED;
|
||||
}
|
||||
}
|
||||
|
||||
private final DBTraceObject object;
|
||||
private final ModuleChangeTranslator translator;
|
||||
|
||||
public DBTraceObjectModule(DBTraceObject object) {
|
||||
this.object = object;
|
||||
|
||||
translator = new ModuleChangeTranslator(object, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Trace getTrace() {
|
||||
return object.getTrace();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceSection addSection(String sectionPath, String sectionName, AddressRange range)
|
||||
throws DuplicateNameException {
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
DBTraceObjectManager manager = object.getManager();
|
||||
List<String> sectionKeyList = PathUtils.parse(sectionPath);
|
||||
if (!PathUtils.isAncestor(object.getCanonicalPath().getKeyList(), sectionKeyList)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Section path must be a successor of this module's path");
|
||||
}
|
||||
return manager.addSection(sectionPath, sectionName, getLifespan(), range);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
return object.getCanonicalPath().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(String name) {
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
object.setValue(getLifespan(), TargetObject.DISPLAY_ATTRIBUTE_NAME, name);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return TraceObjectInterfaceUtils.getValue(object, getLoadedSnap(),
|
||||
TargetObject.DISPLAY_ATTRIBUTE_NAME, String.class, "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRange(AddressRange range) {
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
object.setValue(getLifespan(), TargetModule.RANGE_ATTRIBUTE_NAME, range);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRange getRange() {
|
||||
return TraceObjectInterfaceUtils.getValue(object, getLoadedSnap(),
|
||||
TargetModule.RANGE_ATTRIBUTE_NAME, AddressRange.class, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBase(Address base) {
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
setRange(DBTraceUtils.toRange(base, getMaxAddress()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address getBase() {
|
||||
AddressRange range = getRange();
|
||||
return range == null ? null : range.getMinAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxAddress(Address max) {
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
setRange(DBTraceUtils.toRange(getBase(), max));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address getMaxAddress() {
|
||||
AddressRange range = getRange();
|
||||
return range == null ? null : range.getMaxAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLength(long length) throws AddressOverflowException {
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
setRange(new AddressRangeImpl(getBase(), length));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLength() {
|
||||
return getRange().getLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLifespan(Range<Long> lifespan) throws DuplicateNameException {
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
TraceObjectInterfaceUtils.setLifespan(TraceObjectModule.class, object, lifespan);
|
||||
for (TraceObjectSection section : getSections()) {
|
||||
section.getObject().setLifespan(lifespan);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Range<Long> getLifespan() {
|
||||
return object.getLifespan();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLoadedSnap(long loadedSnap) throws DuplicateNameException {
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
setLifespan(DBTraceUtils.toRange(loadedSnap, getUnloadedSnap()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLoadedSnap() {
|
||||
return object.getMinSnap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUnloadedSnap(long unloadedSnap) throws DuplicateNameException {
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
setLifespan(DBTraceUtils.toRange(getLoadedSnap(), unloadedSnap));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getUnloadedSnap() {
|
||||
return object.getMaxSnap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends TraceObjectSection> getSections() {
|
||||
try (LockHold hold = object.getTrace().lockRead()) {
|
||||
return object.querySuccessorsInterface(getLifespan(), TraceObjectSection.class)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceObjectSection getSectionByName(String sectionName) {
|
||||
PathMatcher matcher = object.getTargetSchema().searchFor(TargetSection.class, true);
|
||||
PathMatcher applied = matcher.applyKeys(List.of(sectionName));
|
||||
return object.getSuccessors(getLifespan(), applied)
|
||||
.map(p -> p.getLastChild(object).queryInterface(TraceObjectSection.class))
|
||||
.findAny()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
object.deleteTree();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceObject getObject() {
|
||||
return object;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceChangeRecord<?, ?> translateEvent(TraceChangeRecord<?, ?> rec) {
|
||||
return translator.translate(rec);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
/* ###
|
||||
* 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.trace.database.module;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.trace.database.target.DBTraceObject;
|
||||
import ghidra.trace.database.target.DBTraceObjectInterface;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.Trace.TraceSectionChangeType;
|
||||
import ghidra.trace.model.modules.*;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.trace.model.target.annot.TraceObjectInterfaceUtils;
|
||||
import ghidra.trace.util.TraceChangeRecord;
|
||||
import ghidra.trace.util.TraceChangeType;
|
||||
import ghidra.util.LockHold;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
public class DBTraceObjectSection implements TraceObjectSection, DBTraceObjectInterface {
|
||||
|
||||
protected class SectionTranslator extends Translator<TraceSection> {
|
||||
protected SectionTranslator(DBTraceObject object, TraceSection iface) {
|
||||
super(TargetSection.RANGE_ATTRIBUTE_NAME, object, iface);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TraceChangeType<TraceSection, Void> getAddedType() {
|
||||
return TraceSectionChangeType.ADDED;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TraceChangeType<TraceSection, Range<Long>> getLifespanChangedType() {
|
||||
return null; // it's the module's lifespan that matters.
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TraceChangeType<TraceSection, Void> getChangedType() {
|
||||
return TraceSectionChangeType.CHANGED;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean appliesToKey(String key) {
|
||||
return TargetSection.RANGE_ATTRIBUTE_NAME.equals(key) ||
|
||||
TargetObject.DISPLAY_ATTRIBUTE_NAME.equals(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TraceChangeType<TraceSection, Void> getDeletedType() {
|
||||
return TraceSectionChangeType.DELETED;
|
||||
}
|
||||
}
|
||||
|
||||
private final DBTraceObject object;
|
||||
private final SectionTranslator translator;
|
||||
|
||||
public DBTraceObjectSection(DBTraceObject object) {
|
||||
this.object = object;
|
||||
|
||||
translator = new SectionTranslator(object, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Trace getTrace() {
|
||||
return object.getTrace();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceModule getModule() {
|
||||
try (LockHold hold = object.getTrace().lockRead()) {
|
||||
return object.queryAncestorsInterface(object.getLifespan(), TraceObjectModule.class)
|
||||
.findAny()
|
||||
.orElseThrow();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
return object.getCanonicalPath().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(String name) throws DuplicateNameException {
|
||||
object.setValue(object.getLifespan(), TargetObject.DISPLAY_ATTRIBUTE_NAME, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return TraceObjectInterfaceUtils.getValue(object, object.getMinSnap(),
|
||||
TargetObject.DISPLAY_ATTRIBUTE_NAME, String.class, "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRange(AddressRange range) {
|
||||
object.setValue(object.getLifespan(), TargetModule.RANGE_ATTRIBUTE_NAME, range);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRange getRange() {
|
||||
return TraceObjectInterfaceUtils.getValue(object, object.getMinSnap(),
|
||||
TargetModule.RANGE_ATTRIBUTE_NAME, AddressRange.class, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
object.deleteTree();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceObject getObject() {
|
||||
return object;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceChangeRecord<?, ?> translateEvent(TraceChangeRecord<?, ?> rec) {
|
||||
return translator.translate(rec);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/* ###
|
||||
* 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.trace.database.module;
|
||||
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.dbg.target.TargetSection;
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.trace.model.modules.TraceSection;
|
||||
import ghidra.trace.model.target.TraceObjectInterface;
|
||||
import ghidra.trace.model.target.annot.TraceObjectInfo;
|
||||
|
||||
@TraceObjectInfo(
|
||||
targetIf = TargetSection.class,
|
||||
shortName = "section",
|
||||
fixedKeys = {
|
||||
TargetObject.DISPLAY_ATTRIBUTE_NAME,
|
||||
TargetSection.RANGE_ATTRIBUTE_NAME
|
||||
})
|
||||
public interface TraceObjectSection extends TraceSection, TraceObjectInterface {
|
||||
void setRange(AddressRange range);
|
||||
}
|
|
@ -38,13 +38,13 @@ import ghidra.program.model.util.CodeUnitInsertionException;
|
|||
import ghidra.program.model.util.PropertyMap;
|
||||
import ghidra.trace.database.DBTrace;
|
||||
import ghidra.trace.database.listing.UndefinedDBTraceData;
|
||||
import ghidra.trace.database.memory.DBTraceMemoryRegion;
|
||||
import ghidra.trace.database.memory.DBTraceMemorySpace;
|
||||
import ghidra.trace.database.symbol.DBTraceFunctionSymbol;
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.model.*;
|
||||
import ghidra.trace.model.listing.*;
|
||||
import ghidra.trace.model.map.TracePropertyMap;
|
||||
import ghidra.trace.model.memory.TraceMemoryRegion;
|
||||
import ghidra.trace.model.program.TraceProgramView;
|
||||
import ghidra.trace.model.program.TraceProgramViewListing;
|
||||
import ghidra.trace.model.symbol.TraceFunctionSymbol;
|
||||
|
@ -79,7 +79,7 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
|
|||
protected final TraceCodeOperations codeOperations;
|
||||
|
||||
protected final DBTraceProgramViewRootModule rootModule;
|
||||
protected final Map<DBTraceMemoryRegion, DBTraceProgramViewFragment> fragmentsByRegion =
|
||||
protected final Map<TraceMemoryRegion, DBTraceProgramViewFragment> fragmentsByRegion =
|
||||
new HashMap<>();
|
||||
|
||||
protected final Map<AddressSnap, UndefinedDBTraceData> undefinedCache =
|
||||
|
@ -779,7 +779,7 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
|
|||
|
||||
@Override
|
||||
public ProgramFragment getFragment(String treeName, Address addr) {
|
||||
DBTraceMemoryRegion region = program.memory.getTopRegion(
|
||||
TraceMemoryRegion region = program.memory.getTopRegion(
|
||||
s -> program.trace.getMemoryManager().getRegionContaining(s, addr));
|
||||
if (region == null) {
|
||||
return null;
|
||||
|
@ -798,7 +798,7 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
|
|||
|
||||
@Override
|
||||
public ProgramFragment getFragment(String treeName, String name) {
|
||||
DBTraceMemoryRegion region = program.memory.getTopRegion(
|
||||
TraceMemoryRegion region = program.memory.getTopRegion(
|
||||
s -> program.trace.getMemoryManager().getLiveRegionByPath(s, name));
|
||||
if (region == null) {
|
||||
return null;
|
||||
|
|
|
@ -29,6 +29,7 @@ import ghidra.program.model.address.*;
|
|||
import ghidra.program.model.mem.*;
|
||||
import ghidra.trace.database.memory.*;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.memory.TraceMemoryRegion;
|
||||
import ghidra.trace.model.program.TraceProgramView;
|
||||
import ghidra.trace.model.program.TraceProgramViewMemory;
|
||||
import ghidra.trace.util.MemoryAdapter;
|
||||
|
@ -53,7 +54,7 @@ public abstract class AbstractDBTraceProgramViewMemory
|
|||
}
|
||||
|
||||
protected void regionBlockRemoved(
|
||||
RemovalNotification<DBTraceMemoryRegion, DBTraceProgramViewMemoryRegionBlock> rn) {
|
||||
RemovalNotification<TraceMemoryRegion, DBTraceProgramViewMemoryRegionBlock> rn) {
|
||||
// Nothing
|
||||
}
|
||||
|
||||
|
@ -138,7 +139,7 @@ public abstract class AbstractDBTraceProgramViewMemory
|
|||
@Override
|
||||
public AddressSetView getExecuteSet() {
|
||||
AddressSet result = new AddressSet();
|
||||
for (DBTraceMemoryRegion region : memoryManager.getRegionsInternal()) {
|
||||
for (TraceMemoryRegion region : memoryManager.getAllRegions()) {
|
||||
if (!region.isExecute() || !program.isRegionVisible(region, region.getLifespan())) {
|
||||
continue;
|
||||
}
|
||||
|
@ -520,8 +521,12 @@ public abstract class AbstractDBTraceProgramViewMemory
|
|||
protected synchronized void changeRange(AddressRange remove, AddressRange add) {
|
||||
if (!forceFullView) {
|
||||
AddressSet temp = new AddressSet(addressSet);
|
||||
temp.delete(remove);
|
||||
temp.add(add);
|
||||
if (remove != null) {
|
||||
temp.delete(remove);
|
||||
}
|
||||
if (add != null) {
|
||||
temp.add(add);
|
||||
}
|
||||
addressSet = temp;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1562,37 +1562,36 @@ public class DBTraceProgramView implements TraceProgramView {
|
|||
trace.removeTransactionListener(listener);
|
||||
}
|
||||
|
||||
public void updateMemoryAddRegionBlock(DBTraceMemoryRegion region) {
|
||||
public void updateMemoryAddRegionBlock(TraceMemoryRegion region) {
|
||||
if (!isRegionVisible(region)) {
|
||||
return;
|
||||
}
|
||||
memory.updateAddRegionBlock(region);
|
||||
}
|
||||
|
||||
public void updateMemoryChangeRegionBlockName(DBTraceMemoryRegion region) {
|
||||
public void updateMemoryChangeRegionBlockName(TraceMemoryRegion region) {
|
||||
if (!isRegionVisible(region)) {
|
||||
return;
|
||||
}
|
||||
memory.updateChangeRegionBlockName(region);
|
||||
}
|
||||
|
||||
public void updateMemoryChangeRegionBlockFlags(DBTraceMemoryRegion region) {
|
||||
if (!isRegionVisible(region)) {
|
||||
public void updateMemoryChangeRegionBlockFlags(TraceMemoryRegion region, Range<Long> lifespan) {
|
||||
if (!isRegionVisible(region, lifespan)) {
|
||||
return;
|
||||
}
|
||||
memory.updateChangeRegionBlockFlags(region);
|
||||
}
|
||||
|
||||
public void updateMemoryChangeRegionBlockRange(DBTraceMemoryRegion region,
|
||||
AddressRange oldRange,
|
||||
AddressRange newRange) {
|
||||
public void updateMemoryChangeRegionBlockRange(TraceMemoryRegion region,
|
||||
AddressRange oldRange, AddressRange newRange) {
|
||||
if (!isRegionVisible(region)) {
|
||||
return;
|
||||
}
|
||||
memory.updateChangeRegionBlockRange(region, oldRange, newRange);
|
||||
}
|
||||
|
||||
public void updateMemoryChangeRegionBlockLifespan(DBTraceMemoryRegion region,
|
||||
public void updateMemoryChangeRegionBlockLifespan(TraceMemoryRegion region,
|
||||
Range<Long> oldLifespan, Range<Long> newLifespan) {
|
||||
boolean inOld = isRegionVisible(region, oldLifespan);
|
||||
boolean inNew = isRegionVisible(region, newLifespan);
|
||||
|
@ -1604,7 +1603,7 @@ public class DBTraceProgramView implements TraceProgramView {
|
|||
}
|
||||
}
|
||||
|
||||
public void updateMemoryDeleteRegionBlock(DBTraceMemoryRegion region) {
|
||||
public void updateMemoryDeleteRegionBlock(TraceMemoryRegion region) {
|
||||
if (!isRegionVisible(region)) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -19,24 +19,24 @@ import java.util.Iterator;
|
|||
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.trace.database.memory.DBTraceMemoryRegion;
|
||||
import ghidra.trace.model.memory.TraceMemoryRegion;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.util.exception.NotFoundException;
|
||||
|
||||
// TODO: Destroy this in favor of databased trees?
|
||||
public class DBTraceProgramViewFragment implements ProgramFragment {
|
||||
protected final AbstractDBTraceProgramViewListing listing;
|
||||
protected final DBTraceMemoryRegion region;
|
||||
protected final TraceMemoryRegion region;
|
||||
|
||||
public DBTraceProgramViewFragment(AbstractDBTraceProgramViewListing listing,
|
||||
DBTraceMemoryRegion region) {
|
||||
TraceMemoryRegion region) {
|
||||
this.listing = listing;
|
||||
this.region = region;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getComment() {
|
||||
return region.description();
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -76,22 +76,27 @@ public class DBTraceProgramViewFragment implements ProgramFragment {
|
|||
|
||||
@Override
|
||||
public boolean contains(Address addr) {
|
||||
return region.contains(addr, listing.program.snap);
|
||||
return region.getRange().contains(addr) &&
|
||||
region.getLifespan().contains(listing.program.snap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Address start, Address end) {
|
||||
// Regions are contiguous
|
||||
long snap = listing.program.snap;
|
||||
return region.contains(start, snap) && region.contains(end, snap);
|
||||
AddressRange range = region.getRange();
|
||||
return range.contains(start) && range.contains(end) &&
|
||||
region.getLifespan().contains(listing.program.snap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(AddressSetView rangeSet) {
|
||||
long snap = listing.program.snap;
|
||||
if (!region.getLifespan().contains(listing.program.snap)) {
|
||||
return false;
|
||||
}
|
||||
for (AddressRange range : rangeSet) {
|
||||
if (!region.contains(range.getMinAddress(), snap) ||
|
||||
!region.contains(range.getMaxAddress(), snap)) {
|
||||
AddressRange regionRange = region.getRange();
|
||||
if (!regionRange.contains(range.getMinAddress()) ||
|
||||
!regionRange.contains(range.getMaxAddress())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,12 +23,12 @@ import com.google.common.cache.CacheBuilder;
|
|||
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.mem.MemoryBlock;
|
||||
import ghidra.trace.database.memory.DBTraceMemoryRegion;
|
||||
import ghidra.trace.model.memory.TraceMemoryRegion;
|
||||
|
||||
public class DBTraceProgramViewMemory extends AbstractDBTraceProgramViewMemory {
|
||||
|
||||
// NB. Keep both per-region and force-full (per-space) block sets ready
|
||||
private final Map<DBTraceMemoryRegion, DBTraceProgramViewMemoryRegionBlock> regionBlocks =
|
||||
private final Map<TraceMemoryRegion, DBTraceProgramViewMemoryRegionBlock> regionBlocks =
|
||||
CacheBuilder.newBuilder()
|
||||
.removalListener(this::regionBlockRemoved)
|
||||
.weakValues()
|
||||
|
@ -45,10 +45,10 @@ public class DBTraceProgramViewMemory extends AbstractDBTraceProgramViewMemory {
|
|||
super(program);
|
||||
}
|
||||
|
||||
protected DBTraceMemoryRegion getTopRegion(Function<Long, DBTraceMemoryRegion> regFunc) {
|
||||
protected TraceMemoryRegion getTopRegion(Function<Long, TraceMemoryRegion> regFunc) {
|
||||
return program.viewport.getTop(s -> {
|
||||
// TODO: There is probably an early-bail condition I can check for.
|
||||
DBTraceMemoryRegion reg = regFunc.apply(s);
|
||||
TraceMemoryRegion reg = regFunc.apply(s);
|
||||
if (reg != null && program.isRegionVisible(reg)) {
|
||||
return reg;
|
||||
}
|
||||
|
@ -56,10 +56,10 @@ public class DBTraceProgramViewMemory extends AbstractDBTraceProgramViewMemory {
|
|||
});
|
||||
}
|
||||
|
||||
protected void forVisibleRegions(Consumer<? super DBTraceMemoryRegion> action) {
|
||||
protected void forVisibleRegions(Consumer<? super TraceMemoryRegion> action) {
|
||||
for (long s : program.viewport.getOrderedSnaps()) {
|
||||
// NOTE: This is slightly faster than new AddressSet(mm.getRegionsAddressSet(snap))
|
||||
for (DBTraceMemoryRegion reg : memoryManager.getRegionsAtSnap(s)) {
|
||||
for (TraceMemoryRegion reg : memoryManager.getRegionsAtSnap(s)) {
|
||||
if (program.isRegionVisible(reg)) {
|
||||
action.accept(reg);
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ public class DBTraceProgramViewMemory extends AbstractDBTraceProgramViewMemory {
|
|||
addressSet = temp;
|
||||
}
|
||||
|
||||
protected MemoryBlock getRegionBlock(DBTraceMemoryRegion region) {
|
||||
protected MemoryBlock getRegionBlock(TraceMemoryRegion region) {
|
||||
return regionBlocks.computeIfAbsent(region,
|
||||
r -> new DBTraceProgramViewMemoryRegionBlock(program, region));
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ public class DBTraceProgramViewMemory extends AbstractDBTraceProgramViewMemory {
|
|||
if (forceFullView) {
|
||||
return getSpaceBlock(addr.getAddressSpace());
|
||||
}
|
||||
DBTraceMemoryRegion region = getTopRegion(s -> memoryManager.getRegionContaining(s, addr));
|
||||
TraceMemoryRegion region = getTopRegion(s -> memoryManager.getRegionContaining(s, addr));
|
||||
return region == null ? null : getRegionBlock(region);
|
||||
}
|
||||
|
||||
|
@ -100,7 +100,7 @@ public class DBTraceProgramViewMemory extends AbstractDBTraceProgramViewMemory {
|
|||
AddressSpace space = program.getAddressFactory().getAddressSpace(blockName);
|
||||
return space == null ? null : getSpaceBlock(space);
|
||||
}
|
||||
DBTraceMemoryRegion region =
|
||||
TraceMemoryRegion region =
|
||||
getTopRegion(s -> memoryManager.getLiveRegionByPath(s, blockName));
|
||||
return region == null ? null : getRegionBlock(region);
|
||||
}
|
||||
|
@ -118,26 +118,26 @@ public class DBTraceProgramViewMemory extends AbstractDBTraceProgramViewMemory {
|
|||
return result.toArray(new MemoryBlock[result.size()]);
|
||||
}
|
||||
|
||||
public void updateAddRegionBlock(DBTraceMemoryRegion region) {
|
||||
public void updateAddRegionBlock(TraceMemoryRegion region) {
|
||||
// TODO: add block to cache?
|
||||
addRange(region.getRange());
|
||||
}
|
||||
|
||||
public void updateChangeRegionBlockName(DBTraceMemoryRegion region) {
|
||||
public void updateChangeRegionBlockName(TraceMemoryRegion region) {
|
||||
// Nothing. Block name is taken from region, uncached
|
||||
}
|
||||
|
||||
public void updateChangeRegionBlockFlags(DBTraceMemoryRegion region) {
|
||||
public void updateChangeRegionBlockFlags(TraceMemoryRegion region) {
|
||||
// Nothing. Block flags are taken from region, uncached
|
||||
}
|
||||
|
||||
public void updateChangeRegionBlockRange(DBTraceMemoryRegion region, AddressRange oldRange,
|
||||
public void updateChangeRegionBlockRange(TraceMemoryRegion region, AddressRange oldRange,
|
||||
AddressRange newRange) {
|
||||
// TODO: update cached block? Nothing to update.
|
||||
changeRange(oldRange, newRange);
|
||||
}
|
||||
|
||||
public void updateDeleteRegionBlock(DBTraceMemoryRegion region) {
|
||||
public void updateDeleteRegionBlock(TraceMemoryRegion region) {
|
||||
regionBlocks.remove(region);
|
||||
removeRange(region.getRange());
|
||||
}
|
||||
|
|
|
@ -20,18 +20,16 @@ import java.math.BigInteger;
|
|||
|
||||
import ghidra.framework.store.LockException;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.trace.database.memory.DBTraceMemoryRegion;
|
||||
import ghidra.trace.database.memory.DBTraceMemorySpace;
|
||||
import ghidra.trace.model.memory.TraceMemoryFlag;
|
||||
import ghidra.trace.model.memory.TraceMemorySpaceInputStream;
|
||||
import ghidra.trace.model.memory.*;
|
||||
|
||||
// TODO: Proper locking all over here
|
||||
public class DBTraceProgramViewMemoryRegionBlock extends AbstractDBTraceProgramViewMemoryBlock {
|
||||
|
||||
private final DBTraceMemoryRegion region;
|
||||
private final TraceMemoryRegion region;
|
||||
|
||||
public DBTraceProgramViewMemoryRegionBlock(DBTraceProgramView program,
|
||||
DBTraceMemoryRegion region) {
|
||||
TraceMemoryRegion region) {
|
||||
super(program);
|
||||
this.region = region;
|
||||
}
|
||||
|
|
|
@ -20,14 +20,14 @@ import com.google.common.collect.Range;
|
|||
import ghidra.program.model.address.*;
|
||||
import ghidra.trace.database.listing.DBTraceCodeRegisterSpace;
|
||||
import ghidra.trace.database.listing.UndefinedDBTraceData;
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.model.program.TraceProgramViewRegisterListing;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class DBTraceProgramViewRegisterListing extends AbstractDBTraceProgramViewListing
|
||||
implements TraceProgramViewRegisterListing {
|
||||
private final DBTraceThread thread;
|
||||
private final TraceThread thread;
|
||||
private Address minAddr;
|
||||
private Address maxAddr;
|
||||
|
||||
|
@ -42,7 +42,7 @@ public class DBTraceProgramViewRegisterListing extends AbstractDBTraceProgramVie
|
|||
}
|
||||
|
||||
@Override
|
||||
public DBTraceThread getThread() {
|
||||
public TraceThread getThread() {
|
||||
return thread;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,6 @@ import ghidra.program.model.util.AddressSetPropertyMap;
|
|||
import ghidra.program.model.util.PropertyMapManager;
|
||||
import ghidra.trace.database.listing.DBTraceCodeRegisterSpace;
|
||||
import ghidra.trace.database.memory.DBTraceMemoryRegisterSpace;
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.data.TraceBasedDataTypeManager;
|
||||
import ghidra.trace.model.program.TraceProgramView;
|
||||
|
@ -50,7 +49,7 @@ public class DBTraceProgramViewRegisters implements TraceProgramView {
|
|||
protected final DomainObjectEventQueues eventQueues;
|
||||
|
||||
private final DBTraceProgramView view;
|
||||
private final DBTraceThread thread;
|
||||
private final TraceThread thread;
|
||||
|
||||
private final DBTraceProgramViewRegisterListing listing;
|
||||
private final DBTraceProgramViewRegisterMemory memory;
|
||||
|
|
|
@ -15,17 +15,17 @@
|
|||
*/
|
||||
package ghidra.trace.database.program;
|
||||
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.model.listing.TraceCodeOperations;
|
||||
import ghidra.trace.model.symbol.TraceReferenceOperations;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
|
||||
public class DBTraceProgramViewRegistersReferenceManager
|
||||
extends AbstractDBTraceProgramViewReferenceManager {
|
||||
|
||||
private final DBTraceThread thread;
|
||||
private final TraceThread thread;
|
||||
|
||||
public DBTraceProgramViewRegistersReferenceManager(DBTraceProgramView program,
|
||||
DBTraceThread thread) {
|
||||
TraceThread thread) {
|
||||
super(program);
|
||||
this.thread = thread;
|
||||
}
|
||||
|
|
|
@ -29,7 +29,6 @@ import ghidra.program.model.address.AddressFactory;
|
|||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.trace.database.*;
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.database.thread.DBTraceThreadManager;
|
||||
import ghidra.trace.model.stack.TraceStackFrame;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
|
@ -134,7 +133,11 @@ public abstract class AbstractDBTraceSpaceBasedManager<M extends DBTraceSpaceBas
|
|||
baseLanguage + ").");
|
||||
}
|
||||
else if (space.isRegisterSpace()) {
|
||||
DBTraceThread thread = threadManager.getThread(ent.threadKey);
|
||||
if (threadManager == null) {
|
||||
Msg.error(this, "Register spaces are not allowed without a thread manager.");
|
||||
continue;
|
||||
}
|
||||
TraceThread thread = threadManager.getThread(ent.threadKey);
|
||||
R regSpace;
|
||||
if (ent.space == null) {
|
||||
regSpace = createRegisterSpace(space, thread, ent);
|
||||
|
@ -190,7 +193,7 @@ public abstract class AbstractDBTraceSpaceBasedManager<M extends DBTraceSpaceBas
|
|||
}
|
||||
|
||||
protected R getForRegisterSpace(TraceThread thread, int frameLevel, boolean createIfAbsent) {
|
||||
DBTraceThread dbThread = trace.getThreadManager().assertIsMine(thread);
|
||||
trace.getThreadManager().assertIsMine(thread);
|
||||
// TODO: What if registers are memory mapped?
|
||||
Pair<TraceThread, Integer> frame = ImmutablePair.of(thread, frameLevel);
|
||||
if (!createIfAbsent) {
|
||||
|
@ -203,8 +206,8 @@ public abstract class AbstractDBTraceSpaceBasedManager<M extends DBTraceSpaceBas
|
|||
AddressSpace regSpace = baseLanguage.getAddressFactory().getRegisterSpace();
|
||||
try {
|
||||
DBTraceSpaceEntry ent = spaceStore.create();
|
||||
ent.set(regSpace.getName(), dbThread.getKey(), frameLevel);
|
||||
return createRegisterSpace(regSpace, dbThread, ent);
|
||||
ent.set(regSpace.getName(), thread.getKey(), frameLevel);
|
||||
return createRegisterSpace(regSpace, thread, ent);
|
||||
}
|
||||
catch (VersionException e) {
|
||||
throw new AssertionError(e);
|
||||
|
@ -256,7 +259,7 @@ public abstract class AbstractDBTraceSpaceBasedManager<M extends DBTraceSpaceBas
|
|||
protected abstract M createSpace(AddressSpace space, DBTraceSpaceEntry ent)
|
||||
throws VersionException, IOException;
|
||||
|
||||
protected abstract R createRegisterSpace(AddressSpace space, DBTraceThread thread,
|
||||
protected abstract R createRegisterSpace(AddressSpace space, TraceThread thread,
|
||||
DBTraceSpaceEntry ent) throws VersionException, IOException;
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,16 +16,16 @@
|
|||
package ghidra.trace.database.space;
|
||||
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.util.TraceAddressSpace;
|
||||
|
||||
public interface DBTraceSpaceKey extends TraceAddressSpace {
|
||||
static class DefaultDBTraceSpaceKey implements DBTraceSpaceKey {
|
||||
private final DBTraceThread thread;
|
||||
private final TraceThread thread;
|
||||
private final AddressSpace space;
|
||||
private final int frameLevel;
|
||||
|
||||
private DefaultDBTraceSpaceKey(DBTraceThread thread, AddressSpace space, int frameLevel) {
|
||||
private DefaultDBTraceSpaceKey(TraceThread thread, AddressSpace space, int frameLevel) {
|
||||
this.thread = thread;
|
||||
this.space = space;
|
||||
this.frameLevel = frameLevel;
|
||||
|
@ -37,7 +37,7 @@ public interface DBTraceSpaceKey extends TraceAddressSpace {
|
|||
}
|
||||
|
||||
@Override
|
||||
public DBTraceThread getThread() {
|
||||
public TraceThread getThread() {
|
||||
return thread;
|
||||
}
|
||||
|
||||
|
@ -47,10 +47,7 @@ public interface DBTraceSpaceKey extends TraceAddressSpace {
|
|||
}
|
||||
}
|
||||
|
||||
static DBTraceSpaceKey create(AddressSpace space, DBTraceThread thread, int frameLevel) {
|
||||
static DBTraceSpaceKey create(AddressSpace space, TraceThread thread, int frameLevel) {
|
||||
return new DefaultDBTraceSpaceKey(thread, space, frameLevel);
|
||||
}
|
||||
|
||||
@Override
|
||||
DBTraceThread getThread();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,234 @@
|
|||
/* ###
|
||||
* 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.trace.database.stack;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import ghidra.dbg.target.TargetStackFrame;
|
||||
import ghidra.dbg.target.schema.TargetObjectSchema;
|
||||
import ghidra.dbg.util.*;
|
||||
import ghidra.trace.database.target.*;
|
||||
import ghidra.trace.model.Trace.TraceStackChangeType;
|
||||
import ghidra.trace.model.stack.*;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.trace.model.thread.TraceObjectThread;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.util.TraceChangeRecord;
|
||||
import ghidra.trace.util.TraceChangeType;
|
||||
import ghidra.util.LockHold;
|
||||
|
||||
public class DBTraceObjectStack implements TraceObjectStack, DBTraceObjectInterface {
|
||||
|
||||
protected class StackChangeTranslator extends Translator<TraceStack> {
|
||||
protected StackChangeTranslator(DBTraceObject object, TraceStack iface) {
|
||||
super(null, object, iface);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TraceChangeType<TraceStack, Void> getAddedType() {
|
||||
return TraceStackChangeType.ADDED;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TraceChangeType<TraceStack, Range<Long>> getLifespanChangedType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TraceChangeType<TraceStack, Void> getChangedType() {
|
||||
return TraceStackChangeType.CHANGED;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean appliesToKey(String key) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TraceChangeType<TraceStack, Void> getDeletedType() {
|
||||
return TraceStackChangeType.DELETED;
|
||||
}
|
||||
}
|
||||
|
||||
private final DBTraceObject object;
|
||||
private final StackChangeTranslator translator;
|
||||
|
||||
public DBTraceObjectStack(DBTraceObject object) {
|
||||
this.object = object;
|
||||
|
||||
translator = new StackChangeTranslator(object, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceThread getThread() {
|
||||
try (LockHold hold = object.getTrace().lockRead()) {
|
||||
return object.queryAncestorsInterface(object.getLifespan(), TraceObjectThread.class)
|
||||
.findAny()
|
||||
.orElseThrow();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSnap() {
|
||||
return object.getMinSnap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDepth() {
|
||||
try (LockHold hold = object.getTrace().lockRead()) {
|
||||
return object
|
||||
.querySuccessorsInterface(object.getLifespan(), TraceObjectStackFrame.class)
|
||||
.map(f -> f.getLevel())
|
||||
.reduce(Integer::max)
|
||||
.map(m -> m + 1)
|
||||
.orElse(0);
|
||||
}
|
||||
}
|
||||
|
||||
protected TraceObjectStackFrame doAddStackFrame(int level) {
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
PathMatcher matcher = object.getTargetSchema().searchFor(TargetStackFrame.class, true);
|
||||
List<String> relKeyList =
|
||||
matcher.applyIndices(PathUtils.makeIndex(level)).getSingletonPath();
|
||||
if (relKeyList == null) {
|
||||
throw new IllegalStateException("Could not determine where to create new frame");
|
||||
}
|
||||
List<String> keyList =
|
||||
PathUtils.extend(object.getCanonicalPath().getKeyList(), relKeyList);
|
||||
return object.getManager().addStackFrame(keyList, getSnap());
|
||||
}
|
||||
}
|
||||
|
||||
protected void copyFrameAttributes(TraceObjectStackFrame from, TraceObjectStackFrame to) {
|
||||
// TODO: All attributes or just those known to StackFrame?
|
||||
to.setProgramCounter(from.getProgramCounter());
|
||||
}
|
||||
|
||||
protected void shiftFrameAttributes(int from, int to, int count,
|
||||
List<TraceObjectStackFrame> frames) {
|
||||
if (from == to) {
|
||||
return;
|
||||
}
|
||||
if (from < to) {
|
||||
for (int i = count - 1; i >= 0; i--) {
|
||||
copyFrameAttributes(frames.get(from + i), frames.get(to + i));
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < count; i++) {
|
||||
copyFrameAttributes(frames.get(from + i), frames.get(to + i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void clearFrameAttributes(int start, int end, List<TraceObjectStackFrame> frames) {
|
||||
for (int i = start; i < end; i++) {
|
||||
TraceObjectStackFrame frame = frames.get(i);
|
||||
frame.setProgramCounter(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDepth(int depth, boolean atInner) {
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
List<TraceObjectStackFrame> frames = // Want mutable list
|
||||
doGetFrames().collect(Collectors.toCollection(ArrayList::new));
|
||||
int curDepth = frames.size();
|
||||
if (curDepth == depth) {
|
||||
return;
|
||||
}
|
||||
if (depth < curDepth) {
|
||||
if (atInner) {
|
||||
int diff = curDepth - depth;
|
||||
shiftFrameAttributes(diff, 0, depth, frames);
|
||||
}
|
||||
for (int i = depth; i < curDepth; i++) {
|
||||
frames.get(i).getObject().deleteTree();
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i = curDepth; i < depth; i++) {
|
||||
frames.add(doAddStackFrame(i));
|
||||
}
|
||||
if (atInner) {
|
||||
int diff = depth - curDepth;
|
||||
shiftFrameAttributes(0, diff, curDepth, frames);
|
||||
clearFrameAttributes(0, diff, frames);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected TraceStackFrame doGetFrame(int level) {
|
||||
TargetObjectSchema schema = object.getTargetSchema();
|
||||
PathPredicates matcher = schema.searchFor(TargetStackFrame.class, true);
|
||||
matcher = matcher.applyIndices(PathUtils.makeIndex(level));
|
||||
return object.getSuccessors(object.getLifespan(), matcher)
|
||||
.findAny()
|
||||
.map(p -> p.getLastChild(object).queryInterface(TraceObjectStackFrame.class))
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
// This assumes the frame indices are contiguous and include 0
|
||||
public TraceStackFrame getFrame(int level, boolean ensureDepth) {
|
||||
if (ensureDepth) {
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
if (level >= getDepth()) {
|
||||
setDepth(level + 1, false);
|
||||
}
|
||||
return doGetFrame(level);
|
||||
}
|
||||
}
|
||||
else {
|
||||
try (LockHold hold = object.getTrace().lockRead()) {
|
||||
return doGetFrame(level);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Stream<TraceObjectStackFrame> doGetFrames() {
|
||||
return object
|
||||
.querySuccessorsInterface(object.getLifespan(), TraceObjectStackFrame.class)
|
||||
.sorted(Comparator.comparing(f -> f.getLevel()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TraceStackFrame> getFrames() {
|
||||
try (LockHold hold = object.getTrace().lockRead()) {
|
||||
return doGetFrames().collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
object.deleteTree();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceObject getObject() {
|
||||
return object;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceChangeRecord<?, ?> translateEvent(TraceChangeRecord<?, ?> rec) {
|
||||
return translator.translate(rec);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
/* ###
|
||||
* 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.trace.database.stack;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.dbg.target.TargetStackFrame;
|
||||
import ghidra.dbg.util.PathUtils;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.CodeUnit;
|
||||
import ghidra.trace.database.target.DBTraceObject;
|
||||
import ghidra.trace.database.target.DBTraceObjectInterface;
|
||||
import ghidra.trace.model.Trace.TraceObjectChangeType;
|
||||
import ghidra.trace.model.Trace.TraceStackChangeType;
|
||||
import ghidra.trace.model.stack.TraceObjectStack;
|
||||
import ghidra.trace.model.stack.TraceObjectStackFrame;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.trace.model.target.TraceObjectValue;
|
||||
import ghidra.trace.model.target.annot.TraceObjectInterfaceUtils;
|
||||
import ghidra.trace.util.TraceAddressSpace;
|
||||
import ghidra.trace.util.TraceChangeRecord;
|
||||
import ghidra.util.LockHold;
|
||||
|
||||
public class DBTraceObjectStackFrame implements TraceObjectStackFrame, DBTraceObjectInterface {
|
||||
private final DBTraceObject object;
|
||||
|
||||
public DBTraceObjectStackFrame(DBTraceObject object) {
|
||||
this.object = object;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceObjectStack getStack() {
|
||||
try (LockHold hold = object.getTrace().lockRead()) {
|
||||
return object
|
||||
.queryCanonicalAncestorsInterface(object.getLifespan(), TraceObjectStack.class)
|
||||
.findAny()
|
||||
.orElseThrow();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLevel() {
|
||||
List<String> keys = object.getCanonicalPath().getKeyList();
|
||||
for (int i = keys.size() - 1; i >= 0; i--) {
|
||||
String k = keys.get(i);
|
||||
if (!PathUtils.isIndex(k)) {
|
||||
continue;
|
||||
}
|
||||
String index = PathUtils.parseIndex(k);
|
||||
try {
|
||||
return Integer.parseInt(index, 10); // TODO: How to know the radix?
|
||||
// TODO: Perhaps just have an attribute that is its level?
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
// fall to preceding key
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("Frame has no index!?");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address getProgramCounter() {
|
||||
return TraceObjectInterfaceUtils.getValue(object, object.getMaxSnap(),
|
||||
TargetStackFrame.PC_ATTRIBUTE_NAME, Address.class, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProgramCounter(Address pc) {
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
if (pc == Address.NO_ADDRESS) {
|
||||
pc = null;
|
||||
}
|
||||
object.setValue(object.getLifespan(), TargetStackFrame.PC_ATTRIBUTE_NAME, pc);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getComment() {
|
||||
// TODO: One day, we'll have dynamic columns in the debugger
|
||||
/**
|
||||
* I don't use an attribute for this, because there's not a nice way track the "identity" of
|
||||
* a stack frame. If the frame is re-used (the recommendation for connector development),
|
||||
* the same comment may not necessarily apply. It'd be nice if the connector re-assigned
|
||||
* levels so that identical objects implied identical frames, but that's quite a burden. The
|
||||
* closest identity heuristic is the program counter. Instead of commenting the frame, I'll
|
||||
* comment the memory at the program counter (often, really the return address). Not
|
||||
* perfect, since it may collide with other comments, but a decent approximation that will
|
||||
* follow the "same frame" as its level changes.
|
||||
*/
|
||||
try (LockHold hold = object.getTrace().lockRead()) {
|
||||
Address pc = getProgramCounter();
|
||||
return pc == null ? null
|
||||
: object.getTrace()
|
||||
.getCommentAdapter()
|
||||
.getComment(object.getMaxSnap(), pc, CodeUnit.EOL_COMMENT);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setComment(String comment) {
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
object.getTrace()
|
||||
.getCommentAdapter()
|
||||
.setComment(object.getLifespan(), getProgramCounter(), CodeUnit.EOL_COMMENT,
|
||||
comment);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceObject getObject() {
|
||||
return object;
|
||||
}
|
||||
|
||||
protected boolean isPcChange(TraceChangeRecord<?, ?> rec) {
|
||||
TraceChangeRecord<TraceObjectValue, Object> cast =
|
||||
TraceObjectChangeType.VALUE_CHANGED.cast(rec);
|
||||
return TargetStackFrame.PC_ATTRIBUTE_NAME.equals(cast.getAffectedObject().getEntryKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceChangeRecord<?, ?> translateEvent(TraceChangeRecord<?, ?> rec) {
|
||||
if (rec.getEventType() == TraceObjectChangeType.CREATED.getType() ||
|
||||
rec.getEventType() == TraceObjectChangeType.DELETED.getType() ||
|
||||
rec.getEventType() == TraceObjectChangeType.VALUE_CHANGED.getType() &&
|
||||
isPcChange(rec)) {
|
||||
// NB. Affected object may be the wrapped object, or the value entry
|
||||
TraceAddressSpace space =
|
||||
spaceForValue(object.getMinSnap(), TargetStackFrame.PC_ATTRIBUTE_NAME);
|
||||
return new TraceChangeRecord<>(TraceStackChangeType.CHANGED, space, getStack(), null,
|
||||
null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -22,7 +22,6 @@ import java.util.*;
|
|||
|
||||
import db.BinaryField;
|
||||
import db.DBRecord;
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.model.Trace.TraceStackChangeType;
|
||||
import ghidra.trace.model.stack.TraceStack;
|
||||
import ghidra.trace.model.stack.TraceStackFrame;
|
||||
|
@ -109,7 +108,7 @@ public class DBTraceStack extends DBAnnotatedObject implements TraceStack {
|
|||
|
||||
private final DBTraceStackManager manager;
|
||||
|
||||
private DBTraceThread thread;
|
||||
private TraceThread thread;
|
||||
private final List<DBTraceStackFrame> frames = new ArrayList<>();
|
||||
|
||||
public DBTraceStack(DBTraceStackManager manager, DBCachedObjectStore<?> store,
|
||||
|
@ -135,7 +134,7 @@ public class DBTraceStack extends DBAnnotatedObject implements TraceStack {
|
|||
}
|
||||
}
|
||||
|
||||
void set(DBTraceThread thread, long snap) {
|
||||
void set(TraceThread thread, long snap) {
|
||||
this.thread = thread;
|
||||
threadSnap.threadKey = thread.getKey();
|
||||
threadSnap.snap = snap;
|
||||
|
@ -169,7 +168,7 @@ public class DBTraceStack extends DBAnnotatedObject implements TraceStack {
|
|||
update(FRAMES_COLUMN);
|
||||
}
|
||||
|
||||
protected void doUpdateFrameDepths(int start, int end) {
|
||||
protected void doUpdateFrameLevels(int start, int end) {
|
||||
for (int i = start; i < end; i++) {
|
||||
frames.get(i).setLevel(i);
|
||||
}
|
||||
|
@ -177,13 +176,13 @@ public class DBTraceStack extends DBAnnotatedObject implements TraceStack {
|
|||
|
||||
@Override
|
||||
public void setDepth(int depth, boolean atInner) {
|
||||
//System.err.println("setDepth(threadKey=" + thread.getKey() + "snap=" + getSnap() +
|
||||
// ",depth=" + depth + ",inner=" + atInner + ");");
|
||||
int curDepth = frameKeys == null ? 0 : frameKeys.length;
|
||||
if (depth == curDepth) {
|
||||
return;
|
||||
}
|
||||
try (LockHold hold = LockHold.lock(manager.lock.writeLock())) {
|
||||
//System.err.println("setDepth(threadKey=" + thread.getKey() + "snap=" + getSnap() +
|
||||
// ",depth=" + depth + ",inner=" + atInner + ");");
|
||||
int curDepth = frameKeys == null ? 0 : frameKeys.length;
|
||||
if (depth == curDepth) {
|
||||
return;
|
||||
}
|
||||
if (depth < curDepth) {
|
||||
List<DBTraceStackFrame> toRemove =
|
||||
atInner ? frames.subList(0, curDepth - depth)
|
||||
|
@ -193,7 +192,7 @@ public class DBTraceStack extends DBAnnotatedObject implements TraceStack {
|
|||
}
|
||||
toRemove.clear();
|
||||
if (atInner) {
|
||||
doUpdateFrameDepths(0, frames.size());
|
||||
doUpdateFrameLevels(0, frames.size());
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -204,11 +203,11 @@ public class DBTraceStack extends DBAnnotatedObject implements TraceStack {
|
|||
}
|
||||
if (atInner) {
|
||||
frames.addAll(0, toAdd);
|
||||
doUpdateFrameDepths(0, frames.size());
|
||||
doUpdateFrameLevels(0, frames.size());
|
||||
}
|
||||
else {
|
||||
frames.addAll(toAdd);
|
||||
doUpdateFrameDepths(frames.size() - toAdd.size(), frames.size());
|
||||
doUpdateFrameLevels(frames.size() - toAdd.size(), frames.size());
|
||||
}
|
||||
}
|
||||
doUpdateFrameKeys();
|
||||
|
|
|
@ -16,21 +16,27 @@
|
|||
package ghidra.trace.database.stack;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import db.DBHandle;
|
||||
import generic.NestedIterator;
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.dbg.util.*;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.trace.database.DBTrace;
|
||||
import ghidra.trace.database.DBTraceManager;
|
||||
import ghidra.trace.database.address.DBTraceOverlaySpaceAdapter;
|
||||
import ghidra.trace.database.stack.DBTraceStack.ThreadSnap;
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.database.thread.DBTraceThreadManager;
|
||||
import ghidra.trace.model.Trace.TraceStackChangeType;
|
||||
import ghidra.trace.model.stack.TraceStackFrame;
|
||||
import ghidra.trace.model.stack.TraceStackManager;
|
||||
import ghidra.trace.model.stack.*;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.trace.model.target.TraceObjectKeyPath;
|
||||
import ghidra.trace.model.thread.TraceObjectThread;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.util.TraceChangeRecord;
|
||||
import ghidra.util.LockHold;
|
||||
|
@ -84,17 +90,63 @@ public class DBTraceStackManager implements TraceStackManager, DBTraceManager {
|
|||
trace.dbError(e);
|
||||
}
|
||||
|
||||
public DBTraceStack getStackByKey(long stackKey) {
|
||||
protected DBTraceStack getStackByKey(long stackKey) {
|
||||
return stackStore.getObjectAt(stackKey);
|
||||
}
|
||||
|
||||
public DBTraceStackFrame getFrameByKey(long frameKey) {
|
||||
protected DBTraceStackFrame getFrameByKey(long frameKey) {
|
||||
return frameStore.getObjectAt(frameKey);
|
||||
}
|
||||
|
||||
public static PathPredicates single(TraceObject seed, Class<? extends TargetObject> targetIf) {
|
||||
PathMatcher stackMatcher = seed.getTargetSchema().searchFor(targetIf, false);
|
||||
PathPattern singleton = stackMatcher.getSingletonPattern();
|
||||
if (singleton.getSingletonPath() == null) {
|
||||
throw new IllegalStateException("Schema doesn't provide a unique " +
|
||||
targetIf.getSimpleName() + " for " + seed.getCanonicalPath());
|
||||
}
|
||||
return singleton;
|
||||
}
|
||||
|
||||
protected TraceObjectStack doGetOrAddObjectStack(TraceThread thread, long snap,
|
||||
boolean createIfAbsent) {
|
||||
TraceObjectThread objThread = (TraceObjectThread) thread;
|
||||
TraceObject obj = objThread.getObject();
|
||||
PathPredicates predicates = single(obj, TargetStack.class);
|
||||
if (createIfAbsent) {
|
||||
try (LockHold hold = trace.lockWrite()) {
|
||||
TraceObjectStack stack =
|
||||
trace.getObjectManager()
|
||||
.getSuccessor(obj, predicates, snap, TraceObjectStack.class);
|
||||
if (stack != null) {
|
||||
return stack;
|
||||
}
|
||||
List<String> keyList = PathUtils.extend(obj.getCanonicalPath().getKeyList(),
|
||||
predicates.getSingletonPath());
|
||||
return trace.getObjectManager().addStack(keyList, snap);
|
||||
}
|
||||
}
|
||||
try (LockHold hold = trace.lockRead()) {
|
||||
return trace.getObjectManager()
|
||||
.getSuccessor(obj, predicates, snap, TraceObjectStack.class);
|
||||
}
|
||||
}
|
||||
|
||||
protected TraceObjectStack doGetLatestObjectStack(TraceThread thread, long snap) {
|
||||
TraceObjectThread objThread = (TraceObjectThread) thread;
|
||||
TraceObject obj = objThread.getObject();
|
||||
List<String> keyList = single(obj, TargetStack.class).getSingletonPath();
|
||||
return trace.getObjectManager()
|
||||
.getLatestSuccessor(obj, TraceObjectKeyPath.of(keyList), snap,
|
||||
TraceObjectStack.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBTraceStack getStack(TraceThread thread, long snap, boolean createIfAbsent) {
|
||||
DBTraceThread dbThread = threadManager.assertIsMine(thread);
|
||||
public TraceStack getStack(TraceThread thread, long snap, boolean createIfAbsent) {
|
||||
threadManager.assertIsMine(thread);
|
||||
if (trace.getObjectManager().hasSchema()) {
|
||||
return doGetOrAddObjectStack(thread, snap, createIfAbsent);
|
||||
}
|
||||
DBTraceStack stack;
|
||||
ThreadSnap key = new ThreadSnap(thread.getKey(), snap);
|
||||
if (createIfAbsent) {
|
||||
|
@ -104,7 +156,7 @@ public class DBTraceStackManager implements TraceStackManager, DBTraceManager {
|
|||
return stack;
|
||||
}
|
||||
stack = stackStore.create();
|
||||
stack.set(dbThread, snap);
|
||||
stack.set(thread, snap);
|
||||
}
|
||||
trace.setChanged(new TraceChangeRecord<>(TraceStackChangeType.ADDED, null, stack));
|
||||
return stack;
|
||||
|
@ -113,22 +165,35 @@ public class DBTraceStackManager implements TraceStackManager, DBTraceManager {
|
|||
}
|
||||
|
||||
@Override
|
||||
public DBTraceStack getLatestStack(TraceThread thread, long snap) {
|
||||
public TraceStack getLatestStack(TraceThread thread, long snap) {
|
||||
threadManager.assertIsMine(thread);
|
||||
DBTraceStack found = stacksByThreadSnap.floorValue(new ThreadSnap(thread.getKey(), snap));
|
||||
if (found == null) {
|
||||
return null;
|
||||
try (LockHold hold = LockHold.lock(lock.readLock())) {
|
||||
if (trace.getObjectManager().hasSchema()) {
|
||||
return doGetLatestObjectStack(thread, snap);
|
||||
}
|
||||
DBTraceStack found =
|
||||
stacksByThreadSnap.floorValue(new ThreadSnap(thread.getKey(), snap));
|
||||
if (found == null) {
|
||||
return null;
|
||||
}
|
||||
if (found.getThread() != thread || found.getSnap() > snap) {
|
||||
// Encoded <thread,snap> field results in unsigned index
|
||||
// NB. Conventionally, a search should never traverse 0 (real to scratch space)
|
||||
return null;
|
||||
}
|
||||
return found;
|
||||
}
|
||||
if (found.getThread() != thread || found.getSnap() > snap) {
|
||||
// Encoded <thread,snap> field results in unsigned index
|
||||
// NB. Conventionally, a search should never traverse 0 (real to scratch space)
|
||||
return null;
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
@Override
|
||||
// TODO: Should probably include a lifespan parameter?
|
||||
public Iterable<TraceStackFrame> getFramesIn(AddressSetView set) {
|
||||
if (trace.getObjectManager().hasSchema()) {
|
||||
return () -> NestedIterator.start(set.iterator(), rng -> trace.getObjectManager()
|
||||
.getObjectsIntersecting(Range.all(), rng, TargetStackFrame.PC_ATTRIBUTE_NAME,
|
||||
TraceObjectStackFrame.class)
|
||||
.iterator());
|
||||
}
|
||||
return () -> NestedIterator.start(set.iterator(), rng -> framesByPC
|
||||
.sub(rng.getMinAddress(), true, rng.getMaxAddress(), true)
|
||||
.values()
|
||||
|
|
|
@ -39,10 +39,10 @@ import ghidra.trace.database.address.DBTraceOverlaySpaceAdapter.DecodesAddresses
|
|||
import ghidra.trace.database.program.DBTraceProgramView;
|
||||
import ghidra.trace.database.symbol.DBTraceSymbolManager.DBTraceSymbolIDEntry;
|
||||
import ghidra.trace.database.symbol.DBTraceSymbolManager.MySymbolTypes;
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.model.Trace.TraceSymbolChangeType;
|
||||
import ghidra.trace.model.TraceAddressSnapRange;
|
||||
import ghidra.trace.model.symbol.TraceSymbol;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.util.TraceAddressSpace;
|
||||
import ghidra.trace.util.TraceChangeRecord;
|
||||
import ghidra.util.LockHold;
|
||||
|
@ -172,7 +172,7 @@ public abstract class AbstractDBTraceSymbol extends DBAnnotatedObject
|
|||
}
|
||||
|
||||
@Override
|
||||
public DBTraceThread getThread() {
|
||||
public TraceThread getThread() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,7 +31,6 @@ import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapSpace;
|
|||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.TraceAddressSnapRangeQuery;
|
||||
import ghidra.trace.database.space.DBTraceSpaceKey;
|
||||
import ghidra.trace.database.symbol.DBTraceSymbolManager.DBTraceSymbolIDEntry;
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.model.TraceAddressSnapRange;
|
||||
import ghidra.trace.model.symbol.TraceNamespaceSymbol;
|
||||
import ghidra.trace.model.symbol.TraceSymbolManager;
|
||||
|
@ -193,11 +192,10 @@ public abstract class AbstractDBTraceSymbolSingleTypeWithLocationView<T extends
|
|||
public Collection<? extends T> getIntersecting(Range<Long> span, TraceThread thread,
|
||||
AddressRange range, boolean includeDynamicSymbols) {
|
||||
try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
|
||||
DBTraceThread dbThread =
|
||||
thread == null ? null : manager.trace.getThreadManager().assertIsMine(thread);
|
||||
manager.assertValidThreadAddress(dbThread, range.getMinAddress()); // Only examines space
|
||||
manager.trace.getThreadManager().assertIsMine(thread);
|
||||
manager.assertValidThreadAddress(thread, range.getMinAddress()); // Only examines space
|
||||
DBTraceAddressSnapRangePropertyMapSpace<Long, DBTraceSymbolIDEntry> space =
|
||||
manager.idMap.get(DBTraceSpaceKey.create(range.getAddressSpace(), dbThread, 0),
|
||||
manager.idMap.get(DBTraceSpaceKey.create(range.getAddressSpace(), thread, 0),
|
||||
false);
|
||||
if (space == null) {
|
||||
return Collections.emptyList();
|
||||
|
@ -214,11 +212,10 @@ public abstract class AbstractDBTraceSymbolSingleTypeWithLocationView<T extends
|
|||
public Collection<? extends T> getIntersecting(Range<Long> span, TraceThread thread,
|
||||
AddressRange range, boolean includeDynamicSymbols, boolean forward) {
|
||||
try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
|
||||
DBTraceThread dbThread =
|
||||
thread == null ? null : manager.trace.getThreadManager().assertIsMine(thread);
|
||||
manager.assertValidThreadAddress(dbThread, range.getMinAddress()); // Only examines space
|
||||
manager.trace.getThreadManager().assertIsMine(thread);
|
||||
manager.assertValidThreadAddress(thread, range.getMinAddress()); // Only examines space
|
||||
DBTraceAddressSnapRangePropertyMapSpace<Long, DBTraceSymbolIDEntry> space =
|
||||
manager.idMap.get(DBTraceSpaceKey.create(range.getAddressSpace(), dbThread, 0),
|
||||
manager.idMap.get(DBTraceSpaceKey.create(range.getAddressSpace(), thread, 0),
|
||||
false);
|
||||
if (space == null) {
|
||||
return Collections.emptyList();
|
||||
|
|
|
@ -30,7 +30,6 @@ import ghidra.program.model.lang.Language;
|
|||
import ghidra.trace.database.DBTrace;
|
||||
import ghidra.trace.database.space.AbstractDBTraceSpaceBasedManager;
|
||||
import ghidra.trace.database.space.DBTraceDelegatingManager;
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.database.thread.DBTraceThreadManager;
|
||||
import ghidra.trace.model.stack.TraceStackFrame;
|
||||
import ghidra.trace.model.symbol.TraceEquateManager;
|
||||
|
@ -116,8 +115,7 @@ public class DBTraceEquateManager
|
|||
|
||||
@Override
|
||||
protected DBTraceEquateRegisterSpace createRegisterSpace(AddressSpace space,
|
||||
DBTraceThread thread,
|
||||
DBTraceSpaceEntry ent) throws VersionException, IOException {
|
||||
TraceThread thread, DBTraceSpaceEntry ent) throws VersionException, IOException {
|
||||
return new DBTraceEquateRegisterSpace(this, dbh, space, ent, thread);
|
||||
}
|
||||
|
||||
|
|
|
@ -20,18 +20,18 @@ import java.io.IOException;
|
|||
import db.DBHandle;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.trace.database.space.AbstractDBTraceSpaceBasedManager.DBTraceSpaceEntry;
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.model.symbol.TraceEquateRegisterSpace;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.util.exception.VersionException;
|
||||
|
||||
public class DBTraceEquateRegisterSpace extends DBTraceEquateSpace
|
||||
implements TraceEquateRegisterSpace {
|
||||
|
||||
protected final DBTraceThread thread;
|
||||
protected final TraceThread thread;
|
||||
private final int frameLevel;
|
||||
|
||||
public DBTraceEquateRegisterSpace(DBTraceEquateManager manager, DBHandle dbh,
|
||||
AddressSpace space, DBTraceSpaceEntry ent, DBTraceThread thread)
|
||||
AddressSpace space, DBTraceSpaceEntry ent, TraceThread thread)
|
||||
throws VersionException, IOException {
|
||||
super(manager, dbh, space, ent);
|
||||
this.thread = thread;
|
||||
|
@ -39,7 +39,7 @@ public class DBTraceEquateRegisterSpace extends DBTraceEquateSpace
|
|||
}
|
||||
|
||||
@Override
|
||||
public DBTraceThread getThread() {
|
||||
public TraceThread getThread() {
|
||||
return thread;
|
||||
}
|
||||
|
||||
|
|
|
@ -33,8 +33,8 @@ import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.Abstract
|
|||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.TraceAddressSnapRangeQuery;
|
||||
import ghidra.trace.database.space.AbstractDBTraceSpaceBasedManager.DBTraceSpaceEntry;
|
||||
import ghidra.trace.database.space.DBTraceSpaceBased;
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.model.symbol.TraceEquateSpace;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.util.LockHold;
|
||||
import ghidra.util.database.*;
|
||||
import ghidra.util.database.annot.*;
|
||||
|
@ -135,7 +135,7 @@ public class DBTraceEquateSpace implements DBTraceSpaceBased, TraceEquateSpace {
|
|||
}
|
||||
|
||||
@Override
|
||||
public DBTraceThread getThread() {
|
||||
public TraceThread getThread() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -28,9 +28,9 @@ import ghidra.trace.database.address.DBTraceOverlaySpaceAdapter.AddressDBFieldCo
|
|||
import ghidra.trace.database.address.DBTraceOverlaySpaceAdapter.DecodesAddresses;
|
||||
import ghidra.trace.database.listing.*;
|
||||
import ghidra.trace.database.space.DBTraceSpaceKey;
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.model.Trace.TraceSymbolChangeType;
|
||||
import ghidra.trace.model.symbol.TraceLabelSymbol;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.util.TraceAddressSpace;
|
||||
import ghidra.trace.util.TraceChangeRecord;
|
||||
import ghidra.util.LockHold;
|
||||
|
@ -70,7 +70,7 @@ public class DBTraceLabelSymbol extends AbstractDBTraceSymbol
|
|||
@DBAnnotatedField(column = END_SNAP_COLUMN_NAME)
|
||||
protected long endSnap;
|
||||
|
||||
protected DBTraceThread thread;
|
||||
protected TraceThread thread;
|
||||
protected Range<Long> lifespan;
|
||||
|
||||
public DBTraceLabelSymbol(DBTraceSymbolManager manager, DBCachedObjectStore<?> store,
|
||||
|
@ -89,7 +89,7 @@ public class DBTraceLabelSymbol extends AbstractDBTraceSymbol
|
|||
lifespan = DBTraceUtils.toRange(startSnap, endSnap);
|
||||
}
|
||||
|
||||
protected void set(Range<Long> lifespan, DBTraceThread thread, Address address, String name,
|
||||
protected void set(Range<Long> lifespan, TraceThread thread, Address address, String name,
|
||||
DBTraceNamespaceSymbol parent, SourceType source) {
|
||||
this.name = name;
|
||||
this.parentID = parent.getID();
|
||||
|
@ -146,7 +146,7 @@ public class DBTraceLabelSymbol extends AbstractDBTraceSymbol
|
|||
}
|
||||
|
||||
@Override
|
||||
public DBTraceThread getThread() {
|
||||
public TraceThread getThread() {
|
||||
return thread;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@ import com.google.common.collect.Range;
|
|||
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.model.Trace.TraceSymbolChangeType;
|
||||
import ghidra.trace.model.symbol.TraceLabelSymbolView;
|
||||
import ghidra.trace.model.symbol.TraceNamespaceSymbol;
|
||||
|
@ -46,13 +45,12 @@ public class DBTraceLabelSymbolView
|
|||
}
|
||||
DBTraceSymbolManager.assertValidName(name);
|
||||
try (LockHold hold = LockHold.lock(manager.lock.writeLock())) {
|
||||
DBTraceThread dbThread =
|
||||
thread == null ? null : manager.trace.getThreadManager().assertIsMine(thread);
|
||||
manager.trace.getThreadManager().assertIsMine(thread);
|
||||
DBTraceNamespaceSymbol dbnsParent = manager.assertIsMine((Namespace) parent);
|
||||
manager.assertValidThreadAddress(dbThread, address);
|
||||
manager.assertValidThreadAddress(thread, address);
|
||||
DBTraceLabelSymbol label = store.create();
|
||||
label.set(lifespan, dbThread, address, name, dbnsParent, source);
|
||||
manager.putID(lifespan, dbThread, address, label.getID());
|
||||
label.set(lifespan, thread, address, name, dbnsParent, source);
|
||||
manager.putID(lifespan, thread, address, label.getID());
|
||||
|
||||
cacheForAt.notifyNewEntry(lifespan, address, label);
|
||||
|
||||
|
|
|
@ -25,10 +25,10 @@ import ghidra.program.model.symbol.*;
|
|||
import ghidra.trace.database.DBTrace;
|
||||
import ghidra.trace.database.DBTraceUtils;
|
||||
import ghidra.trace.database.symbol.DBTraceReferenceSpace.DBTraceReferenceEntry;
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.model.Trace.TraceReferenceChangeType;
|
||||
import ghidra.trace.model.Trace.TraceSymbolChangeType;
|
||||
import ghidra.trace.model.symbol.*;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.util.TraceChangeRecord;
|
||||
import ghidra.util.LockHold;
|
||||
|
||||
|
@ -44,7 +44,7 @@ public class DBTraceReference implements TraceReference {
|
|||
return ent.space.trace;
|
||||
}
|
||||
|
||||
public DBTraceThread getThread() {
|
||||
public TraceThread getThread() {
|
||||
return ent.space.getThread();
|
||||
}
|
||||
|
||||
|
|
|
@ -34,7 +34,6 @@ import ghidra.trace.database.address.DBTraceOverlaySpaceAdapter;
|
|||
import ghidra.trace.database.space.AbstractDBTraceSpaceBasedManager;
|
||||
import ghidra.trace.database.space.DBTraceDelegatingManager;
|
||||
import ghidra.trace.database.symbol.DBTraceReferenceSpace.DBTraceReferenceEntry;
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.database.thread.DBTraceThreadManager;
|
||||
import ghidra.trace.model.stack.TraceStackFrame;
|
||||
import ghidra.trace.model.symbol.TraceReference;
|
||||
|
@ -70,7 +69,7 @@ public class DBTraceReferenceManager extends
|
|||
|
||||
@Override
|
||||
protected DBTraceReferenceRegisterSpace createRegisterSpace(AddressSpace space,
|
||||
DBTraceThread thread, DBTraceSpaceEntry ent) throws VersionException, IOException {
|
||||
TraceThread thread, DBTraceSpaceEntry ent) throws VersionException, IOException {
|
||||
return new DBTraceReferenceRegisterSpace(this, dbh, space, ent, thread);
|
||||
}
|
||||
|
||||
|
|
|
@ -20,17 +20,17 @@ import java.io.IOException;
|
|||
import db.DBHandle;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.trace.database.space.AbstractDBTraceSpaceBasedManager.DBTraceSpaceEntry;
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.model.symbol.TraceReferenceRegisterSpace;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.util.exception.VersionException;
|
||||
|
||||
public class DBTraceReferenceRegisterSpace extends DBTraceReferenceSpace
|
||||
implements TraceReferenceRegisterSpace {
|
||||
protected final DBTraceThread thread;
|
||||
protected final TraceThread thread;
|
||||
private final int frameLevel;
|
||||
|
||||
public DBTraceReferenceRegisterSpace(DBTraceReferenceManager manager, DBHandle dbh,
|
||||
AddressSpace space, DBTraceSpaceEntry ent, DBTraceThread thread)
|
||||
AddressSpace space, DBTraceSpaceEntry ent, TraceThread thread)
|
||||
throws VersionException, IOException {
|
||||
super(manager, dbh, space, ent);
|
||||
this.thread = thread;
|
||||
|
@ -38,7 +38,7 @@ public class DBTraceReferenceRegisterSpace extends DBTraceReferenceSpace
|
|||
}
|
||||
|
||||
@Override
|
||||
public DBTraceThread getThread() {
|
||||
public TraceThread getThread() {
|
||||
return thread;
|
||||
}
|
||||
|
||||
|
|
|
@ -39,11 +39,11 @@ import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.Abstract
|
|||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.TraceAddressSnapRangeQuery;
|
||||
import ghidra.trace.database.space.AbstractDBTraceSpaceBasedManager.DBTraceSpaceEntry;
|
||||
import ghidra.trace.database.space.DBTraceSpaceBased;
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.model.Trace.TraceReferenceChangeType;
|
||||
import ghidra.trace.model.Trace.TraceSymbolChangeType;
|
||||
import ghidra.trace.model.symbol.TraceReference;
|
||||
import ghidra.trace.model.symbol.TraceReferenceSpace;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.util.TraceChangeRecord;
|
||||
import ghidra.util.LockHold;
|
||||
import ghidra.util.database.*;
|
||||
|
@ -389,7 +389,7 @@ public class DBTraceReferenceSpace implements DBTraceSpaceBased, TraceReferenceS
|
|||
}
|
||||
|
||||
@Override
|
||||
public DBTraceThread getThread() {
|
||||
public TraceThread getThread() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -36,12 +36,12 @@ import ghidra.trace.database.map.*;
|
|||
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.DBTraceThread;
|
||||
import ghidra.trace.database.thread.DBTraceThreadManager;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.Trace.TraceFunctionTagChangeType;
|
||||
import ghidra.trace.model.Trace.TraceSymbolChangeType;
|
||||
import ghidra.trace.model.symbol.*;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.util.TraceChangeRecord;
|
||||
import ghidra.util.LockHold;
|
||||
import ghidra.util.database.*;
|
||||
|
@ -661,7 +661,7 @@ public class DBTraceSymbolManager implements TraceSymbolManager, DBTraceManager
|
|||
// DataTypes of Function returns, params, locals, globalRegs
|
||||
}
|
||||
|
||||
protected void assertValidThreadAddress(DBTraceThread thread, Address address) {
|
||||
protected void assertValidThreadAddress(TraceThread thread, Address address) {
|
||||
if (thread != null && address.isMemoryAddress()) {
|
||||
throw new IllegalArgumentException(
|
||||
"Memory addresses cannot be associated with a thread");
|
||||
|
@ -886,7 +886,7 @@ public class DBTraceSymbolManager implements TraceSymbolManager, DBTraceManager
|
|||
|
||||
protected boolean doDeleteSymbol(AbstractDBTraceSymbol symbol) {
|
||||
byte typeID = symbol.getSymbolType().getID();
|
||||
DBTraceThread thread = symbol.getThread();
|
||||
TraceThread thread = symbol.getThread();
|
||||
AbstractDBTraceSymbol deleted = symbolViews.get(typeID).store.deleteKey(symbol.getKey());
|
||||
if (deleted == null) {
|
||||
return false;
|
||||
|
@ -901,21 +901,21 @@ public class DBTraceSymbolManager implements TraceSymbolManager, DBTraceManager
|
|||
return true;
|
||||
}
|
||||
|
||||
protected void putID(Range<Long> lifespan, DBTraceThread thread, Address address, long id) {
|
||||
protected void putID(Range<Long> lifespan, TraceThread thread, Address address, long id) {
|
||||
idMap.get(DBTraceSpaceKey.create(address.getAddressSpace(), thread, 0), true)
|
||||
.put(address, lifespan, id);
|
||||
// TODO: Add to ancestors' too?
|
||||
// NOTE: Might be hard to remove because of overlaps
|
||||
}
|
||||
|
||||
protected void putID(Range<Long> lifespan, DBTraceThread thread, AddressRange rng, long id) {
|
||||
protected void putID(Range<Long> lifespan, TraceThread thread, AddressRange rng, long id) {
|
||||
idMap.get(DBTraceSpaceKey.create(rng.getAddressSpace(), thread, 0), true)
|
||||
.put(rng, lifespan, id);
|
||||
// TODO: Add to ancestors' too?
|
||||
// NOTE: Might be hard to remove because of overlaps
|
||||
}
|
||||
|
||||
protected void delID(DBTraceThread thread, AddressSpace addressSpace, long id) {
|
||||
protected void delID(TraceThread thread, AddressSpace addressSpace, long id) {
|
||||
DBTraceAddressSnapRangePropertyMapSpace<Long, DBTraceSymbolIDEntry> space =
|
||||
idMap.get(DBTraceSpaceKey.create(addressSpace, thread, 0), false);
|
||||
if (space == null) {
|
||||
|
@ -929,7 +929,7 @@ public class DBTraceSymbolManager implements TraceSymbolManager, DBTraceManager
|
|||
}
|
||||
|
||||
protected void assertNotDuplicate(AbstractDBTraceSymbol exclude, Range<Long> lifespan,
|
||||
DBTraceThread thread, Address address, String name, DBTraceNamespaceSymbol parent)
|
||||
TraceThread thread, Address address, String name, DBTraceNamespaceSymbol parent)
|
||||
throws DuplicateNameException {
|
||||
if (address.isMemoryAddress()) {
|
||||
for (AbstractDBTraceSymbol duplicate : labelsAndFunctions.getIntersecting(lifespan,
|
||||
|
|
|
@ -28,7 +28,6 @@ import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapSpace;
|
|||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.TraceAddressSnapRangeQuery;
|
||||
import ghidra.trace.database.space.DBTraceSpaceKey;
|
||||
import ghidra.trace.database.symbol.DBTraceSymbolManager.DBTraceSymbolIDEntry;
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.model.symbol.TraceNamespaceSymbol;
|
||||
import ghidra.trace.model.symbol.TraceSymbolWithLocationView;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
|
@ -80,11 +79,10 @@ public class DBTraceSymbolMultipleTypesWithLocationView<T extends AbstractDBTrac
|
|||
AddressRange range, boolean includeDynamicSymbols, boolean forward) {
|
||||
// NOTE: Do not use Catenated collection, so that the order is by address.
|
||||
try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
|
||||
DBTraceThread dbThread =
|
||||
thread == null ? null : manager.trace.getThreadManager().assertIsMine(thread);
|
||||
manager.assertValidThreadAddress(dbThread, range.getMinAddress()); // Only examines space
|
||||
manager.trace.getThreadManager().assertIsMine(thread);
|
||||
manager.assertValidThreadAddress(thread, range.getMinAddress()); // Only examines space
|
||||
DBTraceAddressSnapRangePropertyMapSpace<Long, DBTraceSymbolIDEntry> space =
|
||||
manager.idMap.get(DBTraceSpaceKey.create(range.getAddressSpace(), dbThread, 0),
|
||||
manager.idMap.get(DBTraceSpaceKey.create(range.getAddressSpace(), thread, 0),
|
||||
false);
|
||||
if (space == null) {
|
||||
return Collections.emptyList();
|
||||
|
|
|
@ -0,0 +1,893 @@
|
|||
/* ###
|
||||
* 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.trace.database.target;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.*;
|
||||
|
||||
import org.apache.commons.collections4.IteratorUtils;
|
||||
|
||||
import com.google.common.collect.Iterators;
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import db.DBRecord;
|
||||
import db.StringField;
|
||||
import ghidra.dbg.target.TargetBreakpointLocation;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.dbg.target.schema.TargetObjectSchema;
|
||||
import ghidra.dbg.util.*;
|
||||
import ghidra.lifecycle.Experimental;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.trace.database.DBTrace;
|
||||
import ghidra.trace.database.DBTraceUtils;
|
||||
import ghidra.trace.database.breakpoint.DBTraceObjectBreakpointLocation;
|
||||
import ghidra.trace.database.breakpoint.DBTraceObjectBreakpointSpec;
|
||||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapSpace;
|
||||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.TraceAddressSnapRangeQuery;
|
||||
import ghidra.trace.database.memory.DBTraceObjectMemoryRegion;
|
||||
import ghidra.trace.database.memory.DBTraceObjectRegister;
|
||||
import ghidra.trace.database.module.*;
|
||||
import ghidra.trace.database.stack.DBTraceObjectStack;
|
||||
import ghidra.trace.database.stack.DBTraceObjectStackFrame;
|
||||
import ghidra.trace.database.target.DBTraceObjectValue.PrimaryTriple;
|
||||
import ghidra.trace.database.target.InternalTraceObjectValue.ValueLifespanSetter;
|
||||
import ghidra.trace.database.target.LifespanCorrector.Direction;
|
||||
import ghidra.trace.database.target.LifespanCorrector.Operation;
|
||||
import ghidra.trace.database.thread.DBTraceObjectThread;
|
||||
import ghidra.trace.model.Trace.TraceObjectChangeType;
|
||||
import ghidra.trace.model.breakpoint.TraceObjectBreakpointLocation;
|
||||
import ghidra.trace.model.breakpoint.TraceObjectBreakpointSpec;
|
||||
import ghidra.trace.model.memory.TraceObjectMemoryRegion;
|
||||
import ghidra.trace.model.memory.TraceObjectRegister;
|
||||
import ghidra.trace.model.modules.TraceObjectModule;
|
||||
import ghidra.trace.model.stack.TraceObjectStack;
|
||||
import ghidra.trace.model.stack.TraceObjectStackFrame;
|
||||
import ghidra.trace.model.target.*;
|
||||
import ghidra.trace.model.target.annot.TraceObjectInterfaceUtils;
|
||||
import ghidra.trace.model.thread.TraceObjectThread;
|
||||
import ghidra.trace.util.TraceChangeRecord;
|
||||
import ghidra.util.LockHold;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.database.*;
|
||||
import ghidra.util.database.DBCachedObjectStoreFactory.AbstractDBFieldCodec;
|
||||
import ghidra.util.database.annot.*;
|
||||
import ghidra.util.database.spatial.rect.Rectangle2DDirection;
|
||||
|
||||
@DBAnnotatedObjectInfo(version = 0)
|
||||
public class DBTraceObject extends DBAnnotatedObject implements TraceObject {
|
||||
protected static final String TABLE_NAME = "Objects";
|
||||
|
||||
protected static <T extends TraceObjectInterface> //
|
||||
Map.Entry<Class<? extends T>, Function<DBTraceObject, ? extends T>> safeEntry(
|
||||
Class<T> cls, Function<DBTraceObject, ? extends T> ctor) {
|
||||
return Map.entry(cls, ctor);
|
||||
}
|
||||
|
||||
protected static final Map<Class<? extends TraceObjectInterface>, //
|
||||
Function<DBTraceObject, ? extends TraceObjectInterface>> CTORS = Map.ofEntries(
|
||||
safeEntry(TraceObjectThread.class, DBTraceObjectThread::new),
|
||||
safeEntry(TraceObjectMemoryRegion.class, DBTraceObjectMemoryRegion::new),
|
||||
safeEntry(TraceObjectModule.class, DBTraceObjectModule::new),
|
||||
safeEntry(TraceObjectSection.class, DBTraceObjectSection::new),
|
||||
safeEntry(TraceObjectBreakpointSpec.class, DBTraceObjectBreakpointSpec::new),
|
||||
safeEntry(TraceObjectBreakpointLocation.class,
|
||||
DBTraceObjectBreakpointLocation::new),
|
||||
safeEntry(TraceObjectStack.class, DBTraceObjectStack::new),
|
||||
safeEntry(TraceObjectStackFrame.class, DBTraceObjectStackFrame::new),
|
||||
safeEntry(TraceObjectRegister.class, DBTraceObjectRegister::new));
|
||||
|
||||
protected static final class ObjectPathDBFieldCodec
|
||||
extends AbstractDBFieldCodec<TraceObjectKeyPath, DBAnnotatedObject, StringField> {
|
||||
|
||||
public ObjectPathDBFieldCodec(Class<DBAnnotatedObject> objectType, Field field,
|
||||
int column) {
|
||||
super(TraceObjectKeyPath.class, objectType, StringField.class, field, column);
|
||||
}
|
||||
|
||||
protected String encode(TraceObjectKeyPath value) {
|
||||
return value == null ? null : value.toString();
|
||||
}
|
||||
|
||||
protected TraceObjectKeyPath decode(String path) {
|
||||
return TraceObjectKeyPath.parse(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void store(TraceObjectKeyPath value, StringField f) {
|
||||
f.setString(encode(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStore(DBAnnotatedObject obj, DBRecord record)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
record.setString(column, encode(getValue(obj)));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doLoad(DBAnnotatedObject obj, DBRecord record)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
setValue(obj, decode(record.getString(column)));
|
||||
}
|
||||
}
|
||||
|
||||
// Canonical path
|
||||
static final String PATH_COLUMN_NAME = "Path";
|
||||
static final String MIN_SNAP_COLUMN_NAME = "MinSnap";
|
||||
static final String MAX_SNAP_COLUMN_NAME = "MaxSnap";
|
||||
|
||||
@DBAnnotatedColumn(PATH_COLUMN_NAME)
|
||||
static DBObjectColumn PATH_COLUMN;
|
||||
@DBAnnotatedColumn(MIN_SNAP_COLUMN_NAME)
|
||||
static DBObjectColumn MIN_SNAP_COLUMN;
|
||||
@DBAnnotatedColumn(MAX_SNAP_COLUMN_NAME)
|
||||
static DBObjectColumn MAX_SNAP_COLUMN;
|
||||
|
||||
@DBAnnotatedField(
|
||||
column = PATH_COLUMN_NAME,
|
||||
codec = ObjectPathDBFieldCodec.class,
|
||||
indexed = true)
|
||||
private TraceObjectKeyPath path;
|
||||
@DBAnnotatedField(column = MIN_SNAP_COLUMN_NAME)
|
||||
private long minSnap;
|
||||
@DBAnnotatedField(column = MAX_SNAP_COLUMN_NAME)
|
||||
private long maxSnap;
|
||||
|
||||
protected final DBTraceObjectManager manager;
|
||||
|
||||
private Range<Long> lifespan;
|
||||
|
||||
private Map<Class<? extends TraceObjectInterface>, TraceObjectInterface> ifaces;
|
||||
|
||||
public DBTraceObject(DBTraceObjectManager manager, DBCachedObjectStore<?> store,
|
||||
DBRecord record) {
|
||||
super(store, record);
|
||||
this.manager = manager;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void fresh(boolean created) throws IOException {
|
||||
if (created) {
|
||||
return;
|
||||
}
|
||||
if (path != null) {
|
||||
freshIfaces();
|
||||
}
|
||||
lifespan = DBTraceUtils.toRange(minSnap, maxSnap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TraceObject: " + getCanonicalPath();
|
||||
}
|
||||
|
||||
protected void freshIfaces() {
|
||||
if (ifaces != null) {
|
||||
return;
|
||||
}
|
||||
Set<Class<? extends TargetObject>> targetIfaces = getTargetSchema().getInterfaces();
|
||||
ifaces = CTORS.entrySet()
|
||||
.stream()
|
||||
.filter(
|
||||
e -> targetIfaces.contains(TraceObjectInterfaceUtils.toTargetIf(e.getKey())))
|
||||
.collect(
|
||||
Collectors.toUnmodifiableMap(e -> e.getKey(), e -> e.getValue().apply(this)));
|
||||
}
|
||||
|
||||
protected void set(TraceObjectKeyPath path, Range<Long> lifespan) {
|
||||
this.path = path;
|
||||
this.lifespan = lifespan;
|
||||
this.doSetLifespan(lifespan);
|
||||
update(PATH_COLUMN);
|
||||
|
||||
freshIfaces();
|
||||
}
|
||||
|
||||
protected void doSetLifespan(Range<Long> lifespan) {
|
||||
this.minSnap = DBTraceUtils.lowerEndpoint(lifespan);
|
||||
this.maxSnap = DBTraceUtils.upperEndpoint(lifespan);
|
||||
update(MIN_SNAP_COLUMN, MAX_SNAP_COLUMN);
|
||||
this.lifespan = DBTraceUtils.toRange(minSnap, maxSnap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBTrace getTrace() {
|
||||
return manager.trace;
|
||||
}
|
||||
|
||||
public DBTraceObjectManager getManager() {
|
||||
return manager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBTraceObject getRoot() {
|
||||
return manager.getRootObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceObjectKeyPath getCanonicalPath() {
|
||||
try (LockHold hold = manager.trace.lockRead()) {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insert(ConflictResolution resolution) {
|
||||
try (LockHold hold = manager.trace.lockWrite()) {
|
||||
for (InternalTraceObjectValue val : getParents()) {
|
||||
if (val.isCanonical() && DBTraceUtils.intersect(val.getLifespan(), lifespan)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
TraceObjectKeyPath parentPath = path.parent();
|
||||
for (DBTraceObject parent : manager.getObjectsByCanonicalPath(parentPath)) {
|
||||
if (DBTraceUtils.intersect(parent.getLifespan(), lifespan)) {
|
||||
parent.setValue(lifespan, path.key(), this, resolution);
|
||||
return;
|
||||
}
|
||||
}
|
||||
DBTraceObject parent = manager.createObject(parentPath, lifespan);
|
||||
parent.setValue(lifespan, path.key(), this, resolution);
|
||||
parent.insert(resolution);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRoot() {
|
||||
try (LockHold hold = manager.trace.lockRead()) {
|
||||
return path.isRoot();
|
||||
}
|
||||
}
|
||||
|
||||
protected Stream<TraceObjectValPath> doGetAllPaths(Range<Long> span,
|
||||
DBTraceObjectValPath post) {
|
||||
if (isRoot()) {
|
||||
return Stream.of(post);
|
||||
}
|
||||
return getParents().stream()
|
||||
.filter(e -> !post.contains(e))
|
||||
.flatMap(e -> e.doGetAllPaths(span, post));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<TraceObjectValPath> getAllPaths(Range<Long> span) {
|
||||
try (LockHold hold = manager.trace.lockRead()) {
|
||||
return doGetAllPaths(span, DBTraceObjectValPath.of());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLifespan(Range<Long> lifespan) {
|
||||
// TODO: Could derive fixed attributes from schema and set their lifespans, too....
|
||||
try (LockHold hold = manager.trace.lockWrite()) {
|
||||
Range<Long> oldLifespan = getLifespan();
|
||||
doSetLifespan(lifespan);
|
||||
emitEvents(new TraceChangeRecord<>(TraceObjectChangeType.LIFESPAN_CHANGED, null, this,
|
||||
oldLifespan, lifespan));
|
||||
}
|
||||
}
|
||||
|
||||
@Experimental
|
||||
public void correctLifespans(Direction direction, Operation operation,
|
||||
ConflictResolution resolution) {
|
||||
new LifespanCorrector(direction, operation, resolution).correctLifespans(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Range<Long> getLifespan() {
|
||||
try (LockHold hold = manager.trace.lockRead()) {
|
||||
return lifespan;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMinSnap(long minSnap) {
|
||||
setLifespan(DBTraceUtils.toRange(minSnap, maxSnap));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMinSnap() {
|
||||
try (LockHold hold = manager.trace.lockRead()) {
|
||||
return minSnap;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxSnap(long maxSnap) {
|
||||
setLifespan(DBTraceUtils.toRange(minSnap, maxSnap));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMaxSnap() {
|
||||
try (LockHold hold = manager.trace.lockRead()) {
|
||||
return maxSnap;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Class<? extends TraceObjectInterface>> getInterfaces() {
|
||||
Set<Class<? extends TargetObject>> targetIfs = getTargetSchema().getInterfaces();
|
||||
return CTORS.keySet()
|
||||
.stream()
|
||||
.filter(iface -> targetIfs.contains(TraceObjectInterfaceUtils.toTargetIf(iface)))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <I extends TraceObjectInterface> I queryInterface(Class<I> ifCls) {
|
||||
return ifCls.cast(ifaces.get(ifCls));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends DBTraceObjectValue> getParents() {
|
||||
return manager.valuesByChild.get(this);
|
||||
}
|
||||
|
||||
protected void collectNonRangedValues(Collection<? super DBTraceObjectValue> result) {
|
||||
for (DBTraceObjectValue val : manager.valuesByTriple
|
||||
.tail(new PrimaryTriple(this, "", Long.MIN_VALUE), true)
|
||||
.values()) {
|
||||
if (val.getParent() != this) {
|
||||
break;
|
||||
}
|
||||
result.add(val);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends InternalTraceObjectValue> getValues() {
|
||||
try (LockHold hold = manager.trace.lockRead()) {
|
||||
List<InternalTraceObjectValue> result = new ArrayList<>();
|
||||
collectNonRangedValues(result);
|
||||
|
||||
for (DBTraceAddressSnapRangePropertyMapSpace<DBTraceObjectAddressRangeValue, //
|
||||
?> space : manager.rangeValueMap
|
||||
.getActiveMemorySpaces()) {
|
||||
for (DBTraceObjectAddressRangeValue val : space.values()) {
|
||||
if (val.getParent() != this) {
|
||||
continue;
|
||||
}
|
||||
result.add(val);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
protected Collection<? extends DBTraceObjectValue> doGetElements() {
|
||||
List<DBTraceObjectValue> result = new ArrayList<>();
|
||||
for (DBTraceObjectValue val : manager.valuesByTriple
|
||||
.sub(new PrimaryTriple(this, "[", Long.MIN_VALUE), true,
|
||||
new PrimaryTriple(this, "\\", Long.MIN_VALUE), false)
|
||||
.values()) {
|
||||
result.add(val);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends DBTraceObjectValue> getElements() {
|
||||
try (LockHold hold = manager.trace.lockRead()) {
|
||||
return doGetElements();
|
||||
}
|
||||
}
|
||||
|
||||
protected Collection<? extends DBTraceObjectValue> doGetAttributes() {
|
||||
List<DBTraceObjectValue> result = new ArrayList<>();
|
||||
for (DBTraceObjectValue val : manager.valuesByTriple
|
||||
.sub(new PrimaryTriple(this, "", Long.MIN_VALUE), true,
|
||||
new PrimaryTriple(this, "[", Long.MIN_VALUE), false)
|
||||
.values()) {
|
||||
result.add(val);
|
||||
}
|
||||
for (DBTraceObjectValue val : manager.valuesByTriple
|
||||
.tail(new PrimaryTriple(this, "\\", Long.MIN_VALUE), true)
|
||||
.values()) {
|
||||
if (val.getParent() != this) {
|
||||
break;
|
||||
}
|
||||
result.add(val);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends DBTraceObjectValue> getAttributes() {
|
||||
try (LockHold hold = manager.trace.lockRead()) {
|
||||
return doGetAttributes();
|
||||
}
|
||||
}
|
||||
|
||||
protected void doCheckConflicts(Range<Long> span, String key, Object value) {
|
||||
for (InternalTraceObjectValue val : doGetValues(span, key)) {
|
||||
if (!Objects.equals(value, val.getValue())) {
|
||||
throw new DuplicateKeyException(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Collection<? extends InternalTraceObjectValue> doGetValues(Range<Long> span,
|
||||
String key) {
|
||||
return doGetValues(DBTraceUtils.lowerEndpoint(span), DBTraceUtils.upperEndpoint(span), key);
|
||||
}
|
||||
|
||||
protected Collection<? extends InternalTraceObjectValue> doGetValues(long lower, long upper,
|
||||
String key) {
|
||||
try (LockHold hold = manager.trace.lockRead()) {
|
||||
// Collect triplet-indexed values
|
||||
Set<InternalTraceObjectValue> result = new LinkedHashSet<>();
|
||||
PrimaryTriple min = new PrimaryTriple(this, key, lower);
|
||||
PrimaryTriple max = new PrimaryTriple(this, key, upper);
|
||||
DBTraceObjectValue floor = manager.valuesByTriple.floorValue(min);
|
||||
if (floor != null && floor.getParent() == this && key.equals(floor.getEntryKey()) &&
|
||||
floor.getLifespan().contains(lower)) {
|
||||
result.add(floor);
|
||||
}
|
||||
for (DBTraceObjectValue val : manager.valuesByTriple.sub(min, true, max, true)
|
||||
.values()) {
|
||||
result.add(val);
|
||||
}
|
||||
|
||||
// Collect R*-Tree-indexed values
|
||||
for (DBTraceAddressSnapRangePropertyMapSpace<DBTraceObjectAddressRangeValue, DBTraceObjectAddressRangeValue> space : manager.rangeValueMap
|
||||
.getActiveMemorySpaces()) {
|
||||
AddressSpace as = space.getAddressSpace();
|
||||
|
||||
for (DBTraceObjectAddressRangeValue val : manager.rangeValueMap
|
||||
.reduce(TraceAddressSnapRangeQuery
|
||||
.intersecting(as.getMinAddress(), as.getMaxAddress(), lower, upper))
|
||||
.values()) {
|
||||
if (val.getParent() != this) {
|
||||
continue;
|
||||
}
|
||||
if (!key.equals(val.getEntryKey())) {
|
||||
continue;
|
||||
}
|
||||
result.add(val);
|
||||
}
|
||||
}
|
||||
|
||||
return result.stream()
|
||||
.sorted(Comparator.comparing(v -> v.getMinSnap()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
protected DBTraceObjectValue doGetNonRangedValue(long snap, String key) {
|
||||
DBTraceObjectValue floor =
|
||||
manager.valuesByTriple.floorValue(new PrimaryTriple(this, key, snap));
|
||||
if (floor == null || floor.getParent() != this || !key.equals(floor.getEntryKey()) ||
|
||||
!floor.getLifespan().contains(snap)) {
|
||||
return null;
|
||||
}
|
||||
return floor;
|
||||
}
|
||||
|
||||
protected Stream<DBTraceObjectValue> doGetNonRangedValues(Range<Long> span, String key,
|
||||
boolean forward) {
|
||||
DBCachedObjectIndex<PrimaryTriple, DBTraceObjectValue> sub = manager.valuesByTriple.sub(
|
||||
new PrimaryTriple(this, key, DBTraceUtils.lowerEndpoint(span)), true,
|
||||
new PrimaryTriple(this, key, DBTraceUtils.upperEndpoint(span)), true);
|
||||
Spliterator<DBTraceObjectValue> spliterator = (forward ? sub : sub.descending())
|
||||
.values()
|
||||
.spliterator();
|
||||
return StreamSupport.stream(spliterator, false);
|
||||
}
|
||||
|
||||
protected DBTraceObjectAddressRangeValue doGetRangedValue(long snap, String key) {
|
||||
for (DBTraceAddressSnapRangePropertyMapSpace<DBTraceObjectAddressRangeValue, //
|
||||
DBTraceObjectAddressRangeValue> space : manager.rangeValueMap
|
||||
.getActiveMemorySpaces()) {
|
||||
AddressSpace as = space.getAddressSpace();
|
||||
for (DBTraceObjectAddressRangeValue val : space
|
||||
.reduce(TraceAddressSnapRangeQuery.atSnap(snap, as))
|
||||
.values()) {
|
||||
if (val.getParent() == this && key.equals(val.getEntryKey())) {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected Stream<DBTraceObjectAddressRangeValue> doGetRangedValues(Range<Long> span,
|
||||
String key, boolean forward) {
|
||||
Rectangle2DDirection dir = forward
|
||||
? Rectangle2DDirection.BOTTOMMOST
|
||||
: Rectangle2DDirection.TOPMOST;
|
||||
List<Iterator<DBTraceObjectAddressRangeValue>> iterators = manager.rangeValueMap
|
||||
.getActiveMemorySpaces()
|
||||
.stream()
|
||||
.map(s -> IteratorUtils.filteredIterator(s
|
||||
.reduce(TraceAddressSnapRangeQuery.intersecting(span, s.getAddressSpace())
|
||||
.starting(dir))
|
||||
.orderedValues()
|
||||
.iterator(),
|
||||
v -> key.equals(v.getEntryKey())))
|
||||
.collect(Collectors.toList());
|
||||
Comparator<Long> order = forward ? Comparator.naturalOrder() : Comparator.reverseOrder();
|
||||
Comparator<DBTraceObjectAddressRangeValue> comparator =
|
||||
Comparator.comparing(v -> v.getMinSnap(), order);
|
||||
Iterator<DBTraceObjectAddressRangeValue> merged =
|
||||
Iterators.mergeSorted(iterators, comparator);
|
||||
return StreamSupport
|
||||
.stream(Spliterators.spliteratorUnknownSize(merged, Spliterator.ORDERED), false)
|
||||
.filter(v -> key.equals(v.getEntryKey()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public InternalTraceObjectValue getValue(long snap, String key) {
|
||||
try (LockHold hold = manager.trace.lockRead()) {
|
||||
DBTraceObjectValue nrVal = doGetNonRangedValue(snap, key);
|
||||
if (nrVal != null) {
|
||||
return nrVal;
|
||||
}
|
||||
return doGetRangedValue(snap, key);
|
||||
}
|
||||
}
|
||||
|
||||
protected Stream<InternalTraceObjectValue> doGetOrderedValues(Range<Long> span, String key,
|
||||
boolean forward) {
|
||||
Stream<DBTraceObjectValue> nrVals = doGetNonRangedValues(span, key, forward);
|
||||
Stream<DBTraceObjectAddressRangeValue> rVals = doGetRangedValues(span, key, forward);
|
||||
Comparator<Long> order = forward ? Comparator.naturalOrder() : Comparator.reverseOrder();
|
||||
Comparator<InternalTraceObjectValue> comparator =
|
||||
Comparator.comparing(v -> v.getMinSnap(), order);
|
||||
Iterator<InternalTraceObjectValue> merged =
|
||||
Iterators.mergeSorted(Arrays.asList(nrVals.iterator(), rVals.iterator()), comparator);
|
||||
return StreamSupport
|
||||
.stream(Spliterators.spliteratorUnknownSize(merged, Spliterator.ORDERED), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<? extends InternalTraceObjectValue> getOrderedValues(Range<Long> span, String key,
|
||||
boolean forward) {
|
||||
try (LockHold hold = manager.trace.lockRead()) {
|
||||
return doGetOrderedValues(span, key, forward);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InternalTraceObjectValue getElement(long snap, String index) {
|
||||
return getValue(snap, PathUtils.makeKey(index));
|
||||
}
|
||||
|
||||
@Override
|
||||
public InternalTraceObjectValue getElement(long snap, long index) {
|
||||
return getElement(snap, PathUtils.makeIndex(index));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceObjectValue getAttribute(long snap, String name) {
|
||||
if (!PathUtils.isName(name)) {
|
||||
throw new IllegalArgumentException("name cannot be an index");
|
||||
}
|
||||
return getValue(snap, name);
|
||||
}
|
||||
|
||||
protected Stream<? extends DBTraceObjectValPath> doGetAncestors(Range<Long> span,
|
||||
DBTraceObjectValPath post, PathPredicates predicates) {
|
||||
if (predicates.matches(getCanonicalPath().getKeyList())) {
|
||||
return Stream.of(post);
|
||||
}
|
||||
if (isRoot()) {
|
||||
return Stream.empty();
|
||||
}
|
||||
return getParents().stream()
|
||||
.filter(e -> !post.contains(e))
|
||||
.flatMap(e -> e.doGetAncestors(span, post, predicates));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<? extends DBTraceObjectValPath> getAncestors(
|
||||
Range<Long> span, PathPredicates rootPredicates) {
|
||||
try (LockHold hold = manager.trace.lockRead()) {
|
||||
return doGetAncestors(span, DBTraceObjectValPath.of(), rootPredicates);
|
||||
}
|
||||
}
|
||||
|
||||
protected Stream<? extends DBTraceObjectValPath> doGetSuccessors(
|
||||
Range<Long> span, DBTraceObjectValPath pre, PathPredicates predicates) {
|
||||
Set<String> nextKeys = predicates.getNextKeys(pre.getKeyList());
|
||||
if (nextKeys.isEmpty()) {
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
Stream<? extends DBTraceObjectValue> attrStream;
|
||||
if (nextKeys.contains("")) {
|
||||
attrStream = doGetAttributes().stream()
|
||||
.filter(v -> DBTraceUtils.intersect(span, v.getLifespan()));
|
||||
}
|
||||
else {
|
||||
attrStream = Stream.empty();
|
||||
}
|
||||
|
||||
Stream<? extends DBTraceObjectValue> elemStream;
|
||||
if (nextKeys.contains("[]")) {
|
||||
elemStream = doGetElements().stream()
|
||||
.filter(v -> DBTraceUtils.intersect(span, v.getLifespan()));
|
||||
}
|
||||
else {
|
||||
elemStream = Stream.empty();
|
||||
}
|
||||
|
||||
Stream<InternalTraceObjectValue> restStream = nextKeys.stream()
|
||||
.filter(k -> !"".equals(k) && !"[]".equals(k))
|
||||
.flatMap(k -> doGetValues(span, k).stream());
|
||||
|
||||
return Stream.concat(Stream.concat(attrStream, elemStream), restStream)
|
||||
.flatMap(v -> v.doGetSuccessors(span, pre, predicates));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<? extends DBTraceObjectValPath> getSuccessors(
|
||||
Range<Long> span, PathPredicates relativePredicates) {
|
||||
try (LockHold hold = manager.trace.lockRead()) {
|
||||
return doGetSuccessors(span, DBTraceObjectValPath.of(), relativePredicates);
|
||||
}
|
||||
}
|
||||
|
||||
protected Stream<? extends DBTraceObjectValPath> doGetOrderedSuccessors(Range<Long> span,
|
||||
DBTraceObjectValPath pre, PathPredicates predicates, boolean forward) {
|
||||
Set<String> nextKeys = predicates.getNextKeys(pre.getKeyList());
|
||||
if (nextKeys.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
if (nextKeys.size() != 1) {
|
||||
throw new IllegalArgumentException("predicates must be a singleton");
|
||||
}
|
||||
String next = nextKeys.iterator().next();
|
||||
if (PathPattern.isWildcard(next)) {
|
||||
throw new IllegalArgumentException("predicates must be a singleton");
|
||||
}
|
||||
return doGetOrderedValues(span, next, forward)
|
||||
.flatMap(v -> v.doGetOrderedSuccessors(span, pre, predicates, forward));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<? extends DBTraceObjectValPath> getOrderedSuccessors(Range<Long> span,
|
||||
TraceObjectKeyPath relativePath, boolean forward) {
|
||||
try (LockHold hold = manager.trace.lockRead()) {
|
||||
return doGetOrderedSuccessors(span, DBTraceObjectValPath.of(),
|
||||
new PathPattern(relativePath.getKeyList()), forward);
|
||||
}
|
||||
}
|
||||
|
||||
protected InternalTraceObjectValue doCreateValue(Range<Long> lifespan, String key,
|
||||
Object value) {
|
||||
return manager.doCreateValue(lifespan, this, key, value);
|
||||
}
|
||||
|
||||
// HACK: Because breakpoint uses address,length instead of range. FIXME!
|
||||
protected void applyBreakpointRangeHack(Range<Long> lifespan, String key, Object value,
|
||||
ConflictResolution resolution) {
|
||||
/**
|
||||
* NOTE: This should only be happening in Target/TraceBreakpointLocation, but I suppose
|
||||
* anything using this scheme should be hacked.
|
||||
*/
|
||||
Address address;
|
||||
int length;
|
||||
if (key == TargetBreakpointLocation.ADDRESS_ATTRIBUTE_NAME &&
|
||||
value instanceof Address) {
|
||||
address = (Address) value;
|
||||
Object lengthObj = getValue(DBTraceUtils.lowerEndpoint(lifespan),
|
||||
TargetBreakpointLocation.LENGTH_ATTRIBUTE_NAME);
|
||||
if (!(lengthObj instanceof Integer)) {
|
||||
return;
|
||||
}
|
||||
length = (Integer) lengthObj;
|
||||
}
|
||||
else if (key == TargetBreakpointLocation.LENGTH_ATTRIBUTE_NAME &&
|
||||
value instanceof Integer) {
|
||||
length = (Integer) value;
|
||||
Object addressObj = getValue(DBTraceUtils.lowerEndpoint(lifespan),
|
||||
TargetBreakpointLocation.ADDRESS_ATTRIBUTE_NAME);
|
||||
if (!(addressObj instanceof Address)) {
|
||||
return;
|
||||
}
|
||||
address = (Address) addressObj;
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
setValue(lifespan, TraceObjectBreakpointLocation.KEY_RANGE,
|
||||
new AddressRangeImpl(address, length), resolution);
|
||||
}
|
||||
catch (AddressOverflowException e) {
|
||||
Msg.warn(this, "Could not set range: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InternalTraceObjectValue setValue(Range<Long> lifespan, String key, Object value,
|
||||
ConflictResolution resolution) {
|
||||
try (LockHold hold = manager.trace.lockWrite()) {
|
||||
if (isDeleted()) {
|
||||
throw new IllegalStateException("Cannot set value on deleted object.");
|
||||
}
|
||||
InternalTraceObjectValue oldEntry = getValue(DBTraceUtils.lowerEndpoint(lifespan), key);
|
||||
Object oldVal = null;
|
||||
if (oldEntry != null && oldEntry.getLifespan().encloses(lifespan)) {
|
||||
oldVal = oldEntry.getValue();
|
||||
}
|
||||
if (resolution == ConflictResolution.DENY) {
|
||||
doCheckConflicts(lifespan, key, value);
|
||||
}
|
||||
InternalTraceObjectValue result = new ValueLifespanSetter(lifespan, value) {
|
||||
@Override
|
||||
protected Iterable<InternalTraceObjectValue> getIntersecting(Long lower,
|
||||
Long upper) {
|
||||
return Collections.unmodifiableCollection(doGetValues(lower, upper, key));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InternalTraceObjectValue create(Range<Long> range, Object value) {
|
||||
return doCreateValue(range, key, value);
|
||||
}
|
||||
}.set(lifespan, value);
|
||||
if (result == null && oldEntry == null) {
|
||||
return null;
|
||||
}
|
||||
emitEvents(new TraceChangeRecord<>(TraceObjectChangeType.VALUE_CHANGED,
|
||||
null, result != null ? result : oldEntry, oldVal, value));
|
||||
|
||||
// NB. It will cause another event. good.
|
||||
applyBreakpointRangeHack(lifespan, key, value, resolution);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceObjectValue setValue(Range<Long> lifespan, String key, Object value) {
|
||||
return setValue(lifespan, key, value, ConflictResolution.TRUNCATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceObjectValue setAttribute(Range<Long> lifespan, String name, Object value) {
|
||||
if (!PathUtils.isName(name)) {
|
||||
throw new IllegalArgumentException("Attribute name must not be an index");
|
||||
}
|
||||
return setValue(lifespan, name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceObjectValue setElement(Range<Long> lifespan, String index, Object value) {
|
||||
return setValue(lifespan, PathUtils.makeKey(index), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceObjectValue setElement(Range<Long> lifespan, long index, Object value) {
|
||||
return setElement(lifespan, PathUtils.makeIndex(index), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TargetObjectSchema getTargetSchema() {
|
||||
return manager.rootSchema.getSuccessorSchema(path.getKeyList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <I extends TraceObjectInterface> Stream<I> queryAncestorsInterface(Range<Long> span,
|
||||
Class<I> ifClass) {
|
||||
Class<? extends TargetObject> targetIf = TraceObjectInterfaceUtils.toTargetIf(ifClass);
|
||||
// This is a sort of meet-in-the-middle. The type search must originate from the root
|
||||
PathMatcher matcher = getManager().getRootSchema().searchFor(targetIf, false);
|
||||
return getAncestors(span, matcher).map(p -> p.getFirstParent(this).queryInterface(ifClass));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <I extends TraceObjectInterface> Stream<I> queryCanonicalAncestorsInterface(
|
||||
Range<Long> span, Class<I> ifClass) {
|
||||
Class<? extends TargetObject> targetIf = TraceObjectInterfaceUtils.toTargetIf(ifClass);
|
||||
// This is a sort of meet-in-the-middle. The type search must originate from the root
|
||||
PathMatcher matcher = getManager().getRootSchema().searchFor(targetIf, false);
|
||||
List<String> parentPath = getCanonicalPath().getKeyList();
|
||||
if (!matcher.ancestorMatches(parentPath, false)) {
|
||||
return Stream.of();
|
||||
}
|
||||
for (; !parentPath.isEmpty(); parentPath = PathUtils.parent(parentPath)) {
|
||||
if (matcher.matches(parentPath)) {
|
||||
return manager.getObjectsByCanonicalPath(TraceObjectKeyPath.of(parentPath))
|
||||
.stream()
|
||||
.filter(o -> DBTraceUtils.intersect(span, o.getLifespan()))
|
||||
.map(o -> o.queryInterface(ifClass))
|
||||
.filter(i -> i != null);
|
||||
}
|
||||
}
|
||||
return Stream.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <I extends TraceObjectInterface> Stream<I> querySuccessorsInterface(Range<Long> span,
|
||||
Class<I> ifClass) {
|
||||
Class<? extends TargetObject> targetIf = TraceObjectInterfaceUtils.toTargetIf(ifClass);
|
||||
PathMatcher matcher = getTargetSchema().searchFor(targetIf, true);
|
||||
return getSuccessors(span, matcher).map(p -> p.getLastChild(this).queryInterface(ifClass))
|
||||
.filter(i -> i != null); // because GP-1301
|
||||
}
|
||||
|
||||
protected void doDelete() {
|
||||
manager.doDeleteObject(this);
|
||||
}
|
||||
|
||||
protected void doDeleteReferringValues() {
|
||||
for (InternalTraceObjectValue child : getValues()) {
|
||||
child.doDelete();
|
||||
}
|
||||
for (DBTraceObjectValue parent : getParents()) {
|
||||
parent.doDelete();
|
||||
}
|
||||
}
|
||||
|
||||
protected void doDeleteSuccessors() {
|
||||
List<DBTraceObjectValue> children = new ArrayList<>();
|
||||
collectNonRangedValues(children);
|
||||
for (DBTraceObjectValue child : children) {
|
||||
child.doDeleteSuccessors();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
try (LockHold hold = LockHold.lock(manager.lock.writeLock())) {
|
||||
doDeleteReferringValues();
|
||||
doDelete();
|
||||
}
|
||||
}
|
||||
|
||||
protected void doDeleteTree() {
|
||||
doDeleteSuccessors();
|
||||
doDeleteReferringValues();
|
||||
doDelete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteTree() {
|
||||
try (LockHold hold = LockHold.lock(manager.lock.writeLock())) {
|
||||
doDeleteTree();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBTraceObject truncateOrDelete(Range<Long> span) {
|
||||
try (LockHold hold = LockHold.lock(manager.lock.writeLock())) {
|
||||
List<Range<Long>> removed = DBTraceUtils.subtract(lifespan, span);
|
||||
if (removed.isEmpty()) {
|
||||
doDelete();
|
||||
return null;
|
||||
}
|
||||
if (removed.size() == 2) {
|
||||
throw new IllegalArgumentException("Cannot create a gap in an object's lifespan");
|
||||
}
|
||||
doSetLifespan(removed.get(0));
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
protected void emitEvents(TraceChangeRecord<?, ?> rec) {
|
||||
manager.trace.setChanged(rec);
|
||||
for (TraceObjectInterface iface : ifaces.values()) {
|
||||
DBTraceObjectInterface dbIface = (DBTraceObjectInterface) iface;
|
||||
TraceChangeRecord<?, ?> evt = dbIface.translateEvent(rec);
|
||||
if (evt != null) {
|
||||
manager.trace.setChanged(evt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,204 @@
|
|||
/* ###
|
||||
* 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.trace.database.target;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import db.DBRecord;
|
||||
import ghidra.dbg.util.PathPredicates;
|
||||
import ghidra.trace.database.DBTraceUtils;
|
||||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree;
|
||||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.AbstractDBTraceAddressSnapRangePropertyMapData;
|
||||
import ghidra.trace.database.target.DBTraceObjectValue.DBTraceObjectDBFieldCodec;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.target.TraceObjectValue;
|
||||
import ghidra.trace.util.TraceAddressSpace;
|
||||
import ghidra.util.LockHold;
|
||||
import ghidra.util.database.DBCachedObjectStore;
|
||||
import ghidra.util.database.DBObjectColumn;
|
||||
import ghidra.util.database.annot.*;
|
||||
|
||||
@DBAnnotatedObjectInfo(version = 0)
|
||||
public class DBTraceObjectAddressRangeValue
|
||||
extends AbstractDBTraceAddressSnapRangePropertyMapData<DBTraceObjectAddressRangeValue>
|
||||
implements InternalTraceObjectValue {
|
||||
public static final String TABLE_NAME = "ObjectRangeValue";
|
||||
|
||||
static final String PARENT_COLUMN_NAME = "ValueParent";
|
||||
static final String KEY_COLUMN_NAME = "ValueKey";
|
||||
static final String TYPE_COLUMN_NAME = "IsAddress";
|
||||
|
||||
@DBAnnotatedColumn(PARENT_COLUMN_NAME)
|
||||
static DBObjectColumn PARENT_COLUMN;
|
||||
@DBAnnotatedColumn(KEY_COLUMN_NAME)
|
||||
static DBObjectColumn KEY_COLUMN;
|
||||
@DBAnnotatedColumn(TYPE_COLUMN_NAME)
|
||||
static DBObjectColumn TYPE_COLUMN;
|
||||
|
||||
@DBAnnotatedField(
|
||||
column = PARENT_COLUMN_NAME,
|
||||
indexed = true,
|
||||
codec = DBTraceObjectDBFieldCodec.class)
|
||||
private DBTraceObject parent;
|
||||
@DBAnnotatedField(column = KEY_COLUMN_NAME)
|
||||
private String entryKey;
|
||||
@DBAnnotatedField(column = TYPE_COLUMN_NAME)
|
||||
private boolean isAddress;
|
||||
|
||||
protected final DBTraceObjectManager manager;
|
||||
|
||||
public DBTraceObjectAddressRangeValue(DBTraceObjectManager manager,
|
||||
DBTraceAddressSnapRangePropertyMapTree<DBTraceObjectAddressRangeValue, ?> tree,
|
||||
DBCachedObjectStore<?> store, DBRecord record) {
|
||||
super(tree, store, record);
|
||||
this.manager = manager;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setRecordValue(DBTraceObjectAddressRangeValue value) {
|
||||
// Nothing to do. I am the value
|
||||
assert value == null;
|
||||
}
|
||||
|
||||
public TraceAddressSpace getTraceAddressSpace() {
|
||||
return tree.getMapSpace();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DBTraceObjectAddressRangeValue getRecordValue() {
|
||||
return this;
|
||||
}
|
||||
|
||||
void set(DBTraceObject parent, String key, boolean isAddress) {
|
||||
this.parent = parent;
|
||||
this.entryKey = key;
|
||||
this.isAddress = isAddress;
|
||||
update(PARENT_COLUMN, KEY_COLUMN, TYPE_COLUMN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Trace getTrace() {
|
||||
return manager.trace;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBTraceObjectManager getManager() {
|
||||
return manager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBTraceObject getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEntryKey() {
|
||||
return entryKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
if (isAddress) {
|
||||
return getRange().getMinAddress();
|
||||
}
|
||||
return getRange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBTraceObject getChild() {
|
||||
throw new ClassCastException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCanonical() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doSetLifespan(Range<Long> lifespan) {
|
||||
super.doSetLifespan(lifespan);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMinSnap(long minSnap) {
|
||||
try (LockHold hold = manager.trace.lockWrite()) {
|
||||
setLifespan(DBTraceUtils.toRange(minSnap, getY2()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMinSnap() {
|
||||
try (LockHold hold = manager.trace.lockRead()) {
|
||||
return getY1();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxSnap(long maxSnap) {
|
||||
try (LockHold hold = manager.trace.lockWrite()) {
|
||||
setLifespan(DBTraceUtils.toRange(getY1(), maxSnap));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMaxSnap() {
|
||||
try (LockHold hold = manager.trace.lockRead()) {
|
||||
return getY2();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<? extends DBTraceObjectValPath> doGetSuccessors(Range<Long> span,
|
||||
DBTraceObjectValPath pre, PathPredicates predicates) {
|
||||
DBTraceObjectValPath path = pre == null ? DBTraceObjectValPath.of() : pre.append(this);
|
||||
if (predicates.matches(path.getKeyList())) {
|
||||
return Stream.of(path);
|
||||
}
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<? extends DBTraceObjectValPath> doGetOrderedSuccessors(Range<Long> span,
|
||||
DBTraceObjectValPath pre, PathPredicates predicates, boolean forward) {
|
||||
return doGetSuccessors(span, pre, predicates);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doDelete() {
|
||||
manager.rangeValueMap.deleteData(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
try (LockHold hold = LockHold.lock(manager.lock.writeLock())) {
|
||||
doDelete();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteTree() {
|
||||
delete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceObjectValue truncateOrDelete(Range<Long> span) {
|
||||
try (LockHold hold = LockHold.lock(manager.lock.writeLock())) {
|
||||
return doTruncateOrDelete(span);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
/* ###
|
||||
* 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.trace.database.target;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import ghidra.trace.model.Trace.TraceObjectChangeType;
|
||||
import ghidra.trace.model.TraceUniqueObject;
|
||||
import ghidra.trace.model.target.*;
|
||||
import ghidra.trace.util.*;
|
||||
import ghidra.util.database.ObjectKey;
|
||||
|
||||
public interface DBTraceObjectInterface extends TraceObjectInterface, TraceUniqueObject {
|
||||
|
||||
abstract class Translator<T> {
|
||||
private final String spaceValueKey;
|
||||
private final DBTraceObject object;
|
||||
private final T iface;
|
||||
|
||||
public Translator(String spaceValueKey, DBTraceObject object, T iface) {
|
||||
this.spaceValueKey = spaceValueKey;
|
||||
this.object = object;
|
||||
this.iface = iface;
|
||||
}
|
||||
|
||||
protected abstract TraceChangeType<T, Void> getAddedType();
|
||||
|
||||
protected abstract TraceChangeType<T, Range<Long>> getLifespanChangedType();
|
||||
|
||||
protected abstract TraceChangeType<T, Void> getChangedType();
|
||||
|
||||
protected abstract boolean appliesToKey(String key);
|
||||
|
||||
protected abstract TraceChangeType<T, Void> getDeletedType();
|
||||
|
||||
public TraceChangeRecord<?, ?> translate(TraceChangeRecord<?, ?> rec) {
|
||||
TraceAddressSpace space = spaceValueKey == null ? null
|
||||
: spaceForValue(object, object.getMinSnap(), spaceValueKey);
|
||||
if (rec.getEventType() == TraceObjectChangeType.CREATED.getType()) {
|
||||
TraceChangeType<T, Void> type = getAddedType();
|
||||
if (type == null) {
|
||||
return null;
|
||||
}
|
||||
assert rec.getAffectedObject() == object;
|
||||
return new TraceChangeRecord<>(type, space, iface, null,
|
||||
null);
|
||||
}
|
||||
if (rec.getEventType() == TraceObjectChangeType.LIFESPAN_CHANGED.getType()) {
|
||||
if (object.isDeleted()) {
|
||||
return null;
|
||||
}
|
||||
TraceChangeType<T, Range<Long>> type = getLifespanChangedType();
|
||||
if (type == null) {
|
||||
return null;
|
||||
}
|
||||
assert rec.getAffectedObject() == object;
|
||||
TraceChangeRecord<TraceObject, Range<Long>> cast =
|
||||
TraceObjectChangeType.LIFESPAN_CHANGED.cast(rec);
|
||||
return new TraceChangeRecord<>(type, space, iface,
|
||||
cast.getOldValue(), cast.getNewValue());
|
||||
}
|
||||
if (rec.getEventType() == TraceObjectChangeType.VALUE_CHANGED.getType()) {
|
||||
if (object.isDeleted()) {
|
||||
return null;
|
||||
}
|
||||
TraceChangeType<T, Void> type = getChangedType();
|
||||
if (type == null) {
|
||||
return null;
|
||||
}
|
||||
TraceChangeRecord<TraceObjectValue, Object> cast =
|
||||
TraceObjectChangeType.VALUE_CHANGED.cast(rec);
|
||||
String key = cast.getAffectedObject().getEntryKey();
|
||||
if (!appliesToKey(key)) {
|
||||
return null;
|
||||
}
|
||||
assert cast.getAffectedObject().getParent() == object;
|
||||
return new TraceChangeRecord<>(type, space, iface, null, null);
|
||||
}
|
||||
if (rec.getEventType() == TraceObjectChangeType.DELETED.getType()) {
|
||||
TraceChangeType<T, Void> type = getDeletedType();
|
||||
if (type == null) {
|
||||
return null;
|
||||
}
|
||||
assert rec.getAffectedObject() == object;
|
||||
return new TraceChangeRecord<>(type, space, iface, null, null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate an object event into the interface-specific event
|
||||
*
|
||||
* <p>
|
||||
* Both the object event and the interface-specific event, if applicable, will be emitted. If
|
||||
* multiple events need to be emitted, then this method may emit them directly via its object's
|
||||
* trace. If exactly one event needs to be emitted, then this method should return the
|
||||
* translated record. If no translation applies, or if the translated event(s) were emitted
|
||||
* directly, this method returns {@code null}.
|
||||
*
|
||||
* @param rec the object event
|
||||
* @return the interface-specific event to emit, or {@code null}
|
||||
*/
|
||||
TraceChangeRecord<?, ?> translateEvent(TraceChangeRecord<?, ?> rec);
|
||||
|
||||
static TraceAddressSpace spaceForValue(TraceObject object, long snap, String key) {
|
||||
TraceObjectValue val = object.getAttribute(snap, key);
|
||||
if (val instanceof DBTraceObjectAddressRangeValue) {
|
||||
DBTraceObjectAddressRangeValue addrVal = (DBTraceObjectAddressRangeValue) val;
|
||||
return addrVal.getTraceAddressSpace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
default TraceAddressSpace spaceForValue(long snap, String key) {
|
||||
return spaceForValue(getObject(), snap, key);
|
||||
}
|
||||
|
||||
@Override
|
||||
default ObjectKey getObjectKey() {
|
||||
return getObject().getObjectKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean isDeleted() {
|
||||
return getObject().isDeleted();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,652 @@
|
|||
/* ###
|
||||
* 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.trace.database.target;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.jdom.JDOMException;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import db.*;
|
||||
import ghidra.dbg.target.TargetBreakpointSpec;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.dbg.target.schema.*;
|
||||
import ghidra.dbg.target.schema.TargetObjectSchema.SchemaName;
|
||||
import ghidra.dbg.util.*;
|
||||
import ghidra.lifecycle.Internal;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.trace.database.DBTrace;
|
||||
import ghidra.trace.database.DBTraceManager;
|
||||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMap;
|
||||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.TraceAddressSnapRangeQuery;
|
||||
import ghidra.trace.database.module.TraceObjectSection;
|
||||
import ghidra.trace.database.target.DBTraceObjectValue.PrimaryTriple;
|
||||
import ghidra.trace.database.thread.DBTraceObjectThread;
|
||||
import ghidra.trace.database.thread.DBTraceThreadManager;
|
||||
import ghidra.trace.model.ImmutableTraceAddressSnapRange;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.Trace.TraceObjectChangeType;
|
||||
import ghidra.trace.model.breakpoint.TraceBreakpointKind;
|
||||
import ghidra.trace.model.breakpoint.TraceObjectBreakpointLocation;
|
||||
import ghidra.trace.model.memory.*;
|
||||
import ghidra.trace.model.modules.TraceObjectModule;
|
||||
import ghidra.trace.model.stack.TraceObjectStack;
|
||||
import ghidra.trace.model.stack.TraceObjectStackFrame;
|
||||
import ghidra.trace.model.target.*;
|
||||
import ghidra.trace.model.target.DuplicateKeyException;
|
||||
import ghidra.trace.model.target.TraceObject.ConflictResolution;
|
||||
import ghidra.trace.model.target.annot.TraceObjectInterfaceUtils;
|
||||
import ghidra.trace.model.thread.TraceObjectThread;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.util.TraceChangeRecord;
|
||||
import ghidra.util.LockHold;
|
||||
import ghidra.util.database.*;
|
||||
import ghidra.util.database.DBCachedObjectStoreFactory.AbstractDBFieldCodec;
|
||||
import ghidra.util.database.DBCachedObjectStoreFactory.PrimitiveCodec;
|
||||
import ghidra.util.database.annot.*;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.util.exception.VersionException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class DBTraceObjectManager implements TraceObjectManager, DBTraceManager {
|
||||
|
||||
public static class DBTraceObjectSchemaDBFieldCodec extends
|
||||
AbstractDBFieldCodec<SchemaContext, DBTraceObjectSchemaEntry, StringField> {
|
||||
public DBTraceObjectSchemaDBFieldCodec(Class<DBTraceObjectSchemaEntry> objectType,
|
||||
Field field, int column) {
|
||||
super(SchemaContext.class, objectType, StringField.class, field, column);
|
||||
}
|
||||
|
||||
protected String encode(SchemaContext value) {
|
||||
return value == null ? null : XmlSchemaContext.serialize(value);
|
||||
}
|
||||
|
||||
protected SchemaContext decode(String xml) {
|
||||
try {
|
||||
return xml == null ? null : XmlSchemaContext.deserialize(xml);
|
||||
}
|
||||
catch (JDOMException e) {
|
||||
throw new IllegalArgumentException("Invalid XML-encoded schema context");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void store(SchemaContext value, StringField f) {
|
||||
f.setString(encode(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStore(DBTraceObjectSchemaEntry obj, DBRecord record)
|
||||
throws IllegalAccessException {
|
||||
record.setString(column, encode(getValue(obj)));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doLoad(DBTraceObjectSchemaEntry obj, DBRecord record)
|
||||
throws IllegalAccessException {
|
||||
setValue(obj, decode(record.getString(column)));
|
||||
}
|
||||
}
|
||||
|
||||
@DBAnnotatedObjectInfo(version = 0)
|
||||
protected static final class DBTraceObjectSchemaEntry extends DBAnnotatedObject {
|
||||
public static final String TABLE_NAME = "ObjectSchema";
|
||||
|
||||
static final String CONTEXT_COLUMN_NAME = "Context";
|
||||
static final String SCHEMA_COLUMN_NAME = "Schema";
|
||||
|
||||
@DBAnnotatedColumn(CONTEXT_COLUMN_NAME)
|
||||
static DBObjectColumn CONTEXT_COLUMN;
|
||||
@DBAnnotatedColumn(SCHEMA_COLUMN_NAME)
|
||||
static DBObjectColumn SCHEMA_COLUMN;
|
||||
|
||||
@DBAnnotatedField(
|
||||
column = CONTEXT_COLUMN_NAME,
|
||||
codec = DBTraceObjectSchemaDBFieldCodec.class)
|
||||
private SchemaContext context;
|
||||
@DBAnnotatedField(column = SCHEMA_COLUMN_NAME)
|
||||
private String schemaName;
|
||||
|
||||
private TargetObjectSchema schema;
|
||||
|
||||
public DBTraceObjectSchemaEntry(DBCachedObjectStore<?> store, DBRecord record) {
|
||||
super(store, record);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void fresh(boolean created) throws IOException {
|
||||
if (created) {
|
||||
return;
|
||||
}
|
||||
schema = context.getSchema(new SchemaName(schemaName));
|
||||
}
|
||||
|
||||
protected void set(TargetObjectSchema schema) {
|
||||
context = schema.getContext();
|
||||
schemaName = schema.getName().toString();
|
||||
update(CONTEXT_COLUMN, SCHEMA_COLUMN);
|
||||
}
|
||||
}
|
||||
|
||||
protected final ReadWriteLock lock;
|
||||
protected final DBTrace trace;
|
||||
|
||||
protected final DBCachedObjectStore<DBTraceObjectSchemaEntry> schemaStore;
|
||||
protected final DBCachedObjectStore<DBTraceObject> objectStore;
|
||||
protected final DBCachedObjectStore<DBTraceObjectValue> valueStore;
|
||||
|
||||
protected final DBTraceAddressSnapRangePropertyMap<DBTraceObjectAddressRangeValue, DBTraceObjectAddressRangeValue> rangeValueMap;
|
||||
|
||||
protected final DBCachedObjectIndex<TraceObjectKeyPath, DBTraceObject> objectsByPath;
|
||||
protected final DBCachedObjectIndex<PrimaryTriple, DBTraceObjectValue> valuesByTriple;
|
||||
protected final DBCachedObjectIndex<DBTraceObject, DBTraceObjectValue> valuesByChild;
|
||||
|
||||
protected final Collection<TraceObject> objectsView;
|
||||
|
||||
protected TargetObjectSchema rootSchema;
|
||||
|
||||
public DBTraceObjectManager(DBHandle dbh, DBOpenMode openMode, ReadWriteLock lock,
|
||||
TaskMonitor monitor, Language baseLanguage, DBTrace trace)
|
||||
throws IOException, VersionException {
|
||||
this.lock = lock;
|
||||
this.trace = trace;
|
||||
|
||||
DBCachedObjectStoreFactory factory = trace.getStoreFactory();
|
||||
schemaStore = factory.getOrCreateCachedStore(DBTraceObjectSchemaEntry.TABLE_NAME,
|
||||
DBTraceObjectSchemaEntry.class, DBTraceObjectSchemaEntry::new, true);
|
||||
loadRootSchema();
|
||||
objectStore = factory.getOrCreateCachedStore(DBTraceObject.TABLE_NAME,
|
||||
DBTraceObject.class, (s, r) -> new DBTraceObject(this, s, r), true);
|
||||
valueStore = factory.getOrCreateCachedStore(DBTraceObjectValue.TABLE_NAME,
|
||||
DBTraceObjectValue.class, (s, r) -> new DBTraceObjectValue(this, s, r), true);
|
||||
rangeValueMap = new DBTraceAddressSnapRangePropertyMap<>(
|
||||
DBTraceObjectAddressRangeValue.TABLE_NAME, dbh, openMode, lock, monitor, baseLanguage,
|
||||
trace, null, DBTraceObjectAddressRangeValue.class,
|
||||
(t, s, r) -> new DBTraceObjectAddressRangeValue(this, t, s, r));
|
||||
|
||||
objectsByPath =
|
||||
objectStore.getIndex(TraceObjectKeyPath.class, DBTraceObject.PATH_COLUMN);
|
||||
valuesByTriple =
|
||||
valueStore.getIndex(PrimaryTriple.class, DBTraceObjectValue.TRIPLE_COLUMN);
|
||||
valuesByChild =
|
||||
valueStore.getIndex(DBTraceObject.class, DBTraceObjectValue.CHILD_COLUMN);
|
||||
|
||||
objectsView = Collections.unmodifiableCollection(objectStore.asMap().values());
|
||||
}
|
||||
|
||||
protected void loadRootSchema() {
|
||||
if (schemaStore.asMap().isEmpty()) {
|
||||
rootSchema = null;
|
||||
return;
|
||||
}
|
||||
assert schemaStore.asMap().size() == 1;
|
||||
DBTraceObjectSchemaEntry schemaEntry = schemaStore.getObjectAt(0);
|
||||
rootSchema = schemaEntry.schema;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dbError(IOException e) {
|
||||
trace.dbError(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateCache(boolean all) {
|
||||
objectStore.invalidateCache();
|
||||
valueStore.invalidateCache();
|
||||
rangeValueMap.invalidateCache(all);
|
||||
schemaStore.invalidateCache();
|
||||
loadRootSchema();
|
||||
}
|
||||
|
||||
@Internal
|
||||
protected DBTraceObject assertIsMine(TraceObject object) {
|
||||
if (!(object instanceof DBTraceObject)) {
|
||||
throw new IllegalArgumentException("Object " + object + " is not part of this trace");
|
||||
}
|
||||
DBTraceObject dbObject = (DBTraceObject) object;
|
||||
if (dbObject.manager != this) {
|
||||
throw new IllegalArgumentException("Object " + object + " is not part of this trace");
|
||||
}
|
||||
if (!getAllObjects().contains(dbObject)) {
|
||||
throw new IllegalArgumentException("Object " + object + " is not part of this trace");
|
||||
}
|
||||
return dbObject;
|
||||
}
|
||||
|
||||
protected Object validatePrimitive(Object child) {
|
||||
PrimitiveCodec.getCodec(child.getClass());
|
||||
return child;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Trace getTrace() {
|
||||
return trace;
|
||||
}
|
||||
|
||||
protected void setSchema(TargetObjectSchema schema) {
|
||||
if (rootSchema != null) {
|
||||
throw new IllegalStateException("There is already a root object");
|
||||
}
|
||||
DBTraceObjectSchemaEntry schemaEntry = schemaStore.create(0);
|
||||
schemaEntry.set(schema);
|
||||
rootSchema = schema;
|
||||
}
|
||||
|
||||
protected InternalTraceObjectValue doCreateValue(Range<Long> lifespan,
|
||||
DBTraceObject parent, String key, Object value) {
|
||||
if (value instanceof AddressRange) {
|
||||
DBTraceObjectAddressRangeValue entry = rangeValueMap
|
||||
.put(new ImmutableTraceAddressSnapRange((AddressRange) value, lifespan), null);
|
||||
entry.set(parent, key, false);
|
||||
return entry;
|
||||
}
|
||||
else if (value instanceof Address) {
|
||||
Address address = (Address) value;
|
||||
AddressRange singleton = new AddressRangeImpl(address, address);
|
||||
DBTraceObjectAddressRangeValue entry = rangeValueMap
|
||||
.put(new ImmutableTraceAddressSnapRange(singleton, lifespan), null);
|
||||
entry.set(parent, key, true);
|
||||
return entry;
|
||||
}
|
||||
DBTraceObjectValue entry = valueStore.create();
|
||||
entry.set(lifespan, parent, key, value);
|
||||
return entry;
|
||||
}
|
||||
|
||||
protected DBTraceObject doCreateObject(TraceObjectKeyPath path, Range<Long> lifespan) {
|
||||
DBTraceObject obj = objectStore.create();
|
||||
obj.set(path, lifespan);
|
||||
obj.emitEvents(new TraceChangeRecord<>(TraceObjectChangeType.CREATED, null, obj));
|
||||
return obj;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBTraceObject createObject(TraceObjectKeyPath path, Range<Long> lifespan) {
|
||||
if (path.isRoot()) {
|
||||
throw new IllegalArgumentException("Cannot create non-root object with root path");
|
||||
}
|
||||
try (LockHold hold = trace.lockWrite()) {
|
||||
if (rootSchema == null) {
|
||||
throw new IllegalStateException("No schema! Create the root object, first.");
|
||||
}
|
||||
return doCreateObject(path, lifespan);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceObjectValue createRootObject(TargetObjectSchema schema) {
|
||||
try (LockHold hold = trace.lockWrite()) {
|
||||
setSchema(schema);
|
||||
DBTraceObject root = doCreateObject(TraceObjectKeyPath.of(), Range.all());
|
||||
assert root.getKey() == 0;
|
||||
InternalTraceObjectValue val = doCreateValue(Range.all(), null, null, root);
|
||||
assert val.getKey() == 0;
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TargetObjectSchema getRootSchema() {
|
||||
try (LockHold hold = trace.lockRead()) {
|
||||
return rootSchema;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBTraceObject getRootObject() {
|
||||
return getObjectById(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBTraceObject getObjectById(long key) {
|
||||
try (LockHold hold = trace.lockRead()) {
|
||||
return objectStore.getObjectAt(key);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends DBTraceObject> getObjectsByCanonicalPath(
|
||||
TraceObjectKeyPath path) {
|
||||
return objectsByPath.get(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<? extends DBTraceObject> getObjectsByPath(Range<Long> span,
|
||||
TraceObjectKeyPath path) {
|
||||
return getValuePaths(span, new PathPattern(path.getKeyList()))
|
||||
.map(p -> p.getLastChild(getRootObject()))
|
||||
.filter(DBTraceObject.class::isInstance)
|
||||
.map(DBTraceObject.class::cast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<? extends DBTraceObjectValPath> getValuePaths(
|
||||
Range<Long> span, PathPredicates predicates) {
|
||||
try (LockHold hold = trace.lockRead()) {
|
||||
DBTraceObjectValue rootVal = valueStore.getObjectAt(0);
|
||||
if (rootVal == null) {
|
||||
return Stream.of();
|
||||
}
|
||||
return rootVal.doGetSuccessors(span, null, predicates);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends TraceObject> getAllObjects() {
|
||||
return objectsView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends TraceObjectValue> getValuesIntersecting(Range<Long> span,
|
||||
AddressRange range) {
|
||||
return Collections.unmodifiableCollection(
|
||||
rangeValueMap.reduce(TraceAddressSnapRangeQuery.intersecting(range, span)).values());
|
||||
}
|
||||
|
||||
public Collection<? extends TraceObjectValue> getValuesAt(long snap, Address address) {
|
||||
return Collections.unmodifiableCollection(
|
||||
rangeValueMap.reduce(TraceAddressSnapRangeQuery.at(address, snap)).values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <I extends TraceObjectInterface> Stream<I> queryAllInterface(Range<Long> span,
|
||||
Class<I> ifClass) {
|
||||
if (rootSchema == null) {
|
||||
throw new IllegalStateException("There is no schema. Create a root object.");
|
||||
}
|
||||
Class<? extends TargetObject> targetIf = TraceObjectInterfaceUtils.toTargetIf(ifClass);
|
||||
PathMatcher matcher = rootSchema.searchFor(targetIf, true);
|
||||
return getValuePaths(span, matcher)
|
||||
.map(p -> p.getLastChild(getRootObject()).queryInterface(ifClass));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
try (LockHold hold = trace.lockWrite()) {
|
||||
valueStore.deleteAll();
|
||||
rangeValueMap.clear();
|
||||
objectStore.deleteAll();
|
||||
schemaStore.deleteAll();
|
||||
rootSchema = null;
|
||||
}
|
||||
}
|
||||
|
||||
protected void doDeleteObject(DBTraceObject object) {
|
||||
objectStore.delete(object);
|
||||
object.emitEvents(new TraceChangeRecord<>(TraceObjectChangeType.DELETED, null, object));
|
||||
}
|
||||
|
||||
protected void doDeleteEdge(DBTraceObjectValue edge) {
|
||||
valueStore.delete(edge);
|
||||
}
|
||||
|
||||
public boolean hasSchema() {
|
||||
return rootSchema != null;
|
||||
}
|
||||
|
||||
protected <I extends TraceObjectInterface> I doAddWithInterface(List<String> keyList,
|
||||
Range<Long> lifespan, Class<I> iface, ConflictResolution resolution) {
|
||||
Class<? extends TargetObject> targetIf = TraceObjectInterfaceUtils.toTargetIf(iface);
|
||||
TargetObjectSchema schema = rootSchema.getSuccessorSchema(keyList);
|
||||
if (!schema.getInterfaces().contains(targetIf)) {
|
||||
throw new IllegalStateException(
|
||||
"Schema " + schema + " at " + PathUtils.toString(keyList) +
|
||||
" does not provide interface " + iface.getSimpleName());
|
||||
}
|
||||
DBTraceObject obj = createObject(TraceObjectKeyPath.of(keyList), lifespan);
|
||||
obj.insert(resolution);
|
||||
return obj.queryInterface(iface);
|
||||
}
|
||||
|
||||
protected <I extends TraceObjectInterface> I doAddWithInterface(String path,
|
||||
Range<Long> lifespan, Class<I> iface, ConflictResolution resolution) {
|
||||
return doAddWithInterface(PathUtils.parse(path), lifespan, iface, resolution);
|
||||
}
|
||||
|
||||
public <I extends TraceObjectInterface> Collection<I> getAllObjects(Class<I> iface) {
|
||||
try (LockHold hold = trace.lockRead()) {
|
||||
return queryAllInterface(Range.all(), iface).collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
|
||||
public <I extends TraceObjectInterface> Collection<I> getObjectsByPath(String path,
|
||||
Class<I> iface) {
|
||||
try (LockHold hold = trace.lockRead()) {
|
||||
return getObjectsByPath(Range.all(), TraceObjectKeyPath.parse(path))
|
||||
.map(o -> o.queryInterface(iface))
|
||||
.filter(i -> i != null)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
|
||||
public <I extends TraceObjectInterface> I getObjectByPath(long snap, String path,
|
||||
Class<I> iface) {
|
||||
try (LockHold hold = trace.lockRead()) {
|
||||
return getObjectsByPath(Range.singleton(snap), TraceObjectKeyPath.parse(path)).findAny()
|
||||
.map(o -> o.queryInterface(iface))
|
||||
.orElse(null);
|
||||
}
|
||||
}
|
||||
|
||||
protected <I extends TraceObjectInterface> Stream<I> doParentsWithKeyHaving(
|
||||
Stream<? extends TraceObjectValue> values, String key, Class<I> iface) {
|
||||
return values.filter(v -> key.equals(v.getEntryKey()))
|
||||
.map(v -> v.getParent())
|
||||
.map(o -> o.queryInterface(iface))
|
||||
.filter(i -> i != null);
|
||||
}
|
||||
|
||||
public <I extends TraceObjectInterface> Collection<I> getObjectsContaining(long snap,
|
||||
Address address, String key, Class<I> iface) {
|
||||
try (LockHold hold = trace.lockRead()) {
|
||||
return doParentsWithKeyHaving(getValuesAt(snap, address).stream(), key,
|
||||
iface).collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
|
||||
public <I extends TraceObjectInterface> I getObjectContaining(long snap, Address address,
|
||||
String key, Class<I> iface) {
|
||||
try (LockHold hold = trace.lockRead()) {
|
||||
return doParentsWithKeyHaving(getValuesAt(snap, address).stream(), key,
|
||||
iface).findAny().orElse(null);
|
||||
}
|
||||
}
|
||||
|
||||
public <I extends TraceObjectInterface> Collection<I> getObjectsIntersecting(
|
||||
Range<Long> lifespan, AddressRange range, String key, Class<I> iface) {
|
||||
try (LockHold hold = trace.lockRead()) {
|
||||
return doParentsWithKeyHaving(getValuesIntersecting(lifespan, range).stream(), key,
|
||||
iface).collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
|
||||
public <I extends TraceObjectInterface> Collection<I> getObjectsAtSnap(long snap,
|
||||
Class<I> iface) {
|
||||
try (LockHold hold = trace.lockRead()) {
|
||||
return queryAllInterface(Range.singleton(snap), iface).collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
|
||||
public <I extends TraceObjectInterface> AddressSetView getObjectsAddressSet(long snap,
|
||||
String key, Class<I> ifaceCls, Predicate<? super I> predicate) {
|
||||
return rangeValueMap.getAddressSetView(Range.singleton(snap), v -> {
|
||||
if (!key.equals(v.getEntryKey())) {
|
||||
return false;
|
||||
}
|
||||
TraceObject parent = v.getParent();
|
||||
I iface = parent.queryInterface(ifaceCls);
|
||||
if (iface == null) {
|
||||
return false;
|
||||
}
|
||||
if (!predicate.test(iface)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public <I extends TraceObjectInterface> I getSuccessor(TraceObject seed,
|
||||
PathPredicates predicates, long snap, Class<I> iface) {
|
||||
try (LockHold hold = trace.lockRead()) {
|
||||
return seed.getSuccessors(Range.singleton(snap), predicates)
|
||||
.map(p -> p.getLastChild(seed).queryInterface(iface))
|
||||
.filter(i -> i != null)
|
||||
.findAny()
|
||||
.orElse(null);
|
||||
}
|
||||
}
|
||||
|
||||
public <I extends TraceObjectInterface> I getLatestSuccessor(TraceObject seed,
|
||||
TraceObjectKeyPath path, long snap, Class<I> iface) {
|
||||
try (LockHold hold = trace.lockRead()) {
|
||||
return seed.getOrderedSuccessors(Range.atMost(snap), path, false)
|
||||
.map(p -> p.getLastChild(seed).queryInterface(iface))
|
||||
.filter(i -> i != null)
|
||||
.findAny()
|
||||
.orElse(null);
|
||||
}
|
||||
}
|
||||
|
||||
public TraceObjectBreakpointLocation addBreakpoint(String path, Range<Long> lifespan,
|
||||
AddressRange range, Collection<TraceThread> threads,
|
||||
Collection<TraceBreakpointKind> kinds, boolean enabled, String comment)
|
||||
throws DuplicateNameException {
|
||||
// First verify that the schema accommodates
|
||||
List<String> specPath =
|
||||
getRootSchema().searchForAncestor(TargetBreakpointSpec.class, PathUtils.parse(path));
|
||||
if (specPath == null) {
|
||||
throw new IllegalStateException("The schema does not provide an implicit " +
|
||||
"breakpoint specification on the given path.");
|
||||
}
|
||||
try (LockHold hold = trace.lockWrite()) {
|
||||
TraceObjectBreakpointLocation loc = doAddWithInterface(path, lifespan,
|
||||
TraceObjectBreakpointLocation.class, ConflictResolution.DENY);
|
||||
loc.setName(path);
|
||||
loc.setRange(range);
|
||||
// NB. Ignore threads. I'd like to deprecate that field, anyway.
|
||||
loc.setKinds(kinds);
|
||||
loc.setEnabled(enabled);
|
||||
loc.setComment(comment);
|
||||
return loc;
|
||||
}
|
||||
catch (DuplicateKeyException e) {
|
||||
throw new DuplicateNameException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public TraceObjectMemoryRegion addMemoryRegion(String path, Range<Long> lifespan,
|
||||
AddressRange range, Collection<TraceMemoryFlag> flags)
|
||||
throws TraceOverlappedRegionException {
|
||||
try (LockHold hold = trace.lockWrite()) {
|
||||
TraceObjectMemoryRegion region = doAddWithInterface(path, lifespan,
|
||||
TraceObjectMemoryRegion.class, ConflictResolution.TRUNCATE);
|
||||
/**
|
||||
* TODO: Test that when the ADDED events hits, that it actually appears in queries. I
|
||||
* suspect this will work since the events and/or event processors should be delayed
|
||||
* until the write lock is released. Certainly, a query would require the read lock.
|
||||
*/
|
||||
region.setName(path);
|
||||
region.setRange(range);
|
||||
region.setFlags(flags);
|
||||
trace.updateViewsAddRegionBlock(region);
|
||||
return region;
|
||||
}
|
||||
}
|
||||
|
||||
public TraceObjectModule addModule(String path, String name, Range<Long> lifespan,
|
||||
AddressRange range) throws DuplicateNameException {
|
||||
try (LockHold hold = trace.lockWrite()) {
|
||||
TraceObjectModule module = doAddWithInterface(path, lifespan, TraceObjectModule.class,
|
||||
ConflictResolution.DENY);
|
||||
module.setName(name);
|
||||
module.setRange(range);
|
||||
return module;
|
||||
}
|
||||
catch (DuplicateKeyException e) {
|
||||
throw new DuplicateNameException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public TraceObjectSection addSection(String path, String name, Range<Long> lifespan,
|
||||
AddressRange range) throws DuplicateNameException {
|
||||
try (LockHold hold = trace.lockWrite()) {
|
||||
TraceObjectSection section = doAddWithInterface(path, lifespan,
|
||||
TraceObjectSection.class, ConflictResolution.DENY);
|
||||
section.setName(name);
|
||||
section.setRange(range);
|
||||
return section;
|
||||
}
|
||||
catch (DuplicateKeyException e) {
|
||||
throw new DuplicateNameException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public TraceObjectStack addStack(List<String> keyList, long snap) {
|
||||
try (LockHold hold = trace.lockWrite()) {
|
||||
return doAddWithInterface(keyList, Range.singleton(snap), TraceObjectStack.class,
|
||||
ConflictResolution.DENY);
|
||||
}
|
||||
}
|
||||
|
||||
public TraceObjectStackFrame addStackFrame(List<String> keyList, long snap) {
|
||||
try (LockHold hold = trace.lockWrite()) {
|
||||
return doAddWithInterface(keyList, Range.singleton(snap), TraceObjectStackFrame.class,
|
||||
ConflictResolution.DENY);
|
||||
}
|
||||
}
|
||||
|
||||
public TraceObjectThread addThread(String path, String display, Range<Long> lifespan)
|
||||
throws DuplicateNameException {
|
||||
try (LockHold hold = trace.lockWrite()) {
|
||||
TraceObjectThread thread = doAddWithInterface(path, lifespan, TraceObjectThread.class,
|
||||
ConflictResolution.DENY);
|
||||
thread.setName(display);
|
||||
return thread;
|
||||
}
|
||||
catch (DuplicateKeyException e) {
|
||||
throw new DuplicateNameException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean checkMyObject(DBTraceObject object) {
|
||||
if (object.manager != this) {
|
||||
return false;
|
||||
}
|
||||
if (!getAllObjects().contains(object)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public TraceThread assertMyThread(TraceThread thread) {
|
||||
if (!(thread instanceof DBTraceObjectThread)) {
|
||||
throw new AssertionError("Thread " + thread + " is not an object in this trace");
|
||||
}
|
||||
DBTraceObjectThread dbThread = (DBTraceObjectThread) thread;
|
||||
if (!checkMyObject(dbThread.getObject())) {
|
||||
throw new AssertionError("Thread " + thread + " is not an object in this trace");
|
||||
}
|
||||
return dbThread;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
/* ###
|
||||
* 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.trace.database.target;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import ghidra.dbg.util.PathUtils.PathComparator;
|
||||
import ghidra.trace.model.target.*;
|
||||
|
||||
public class DBTraceObjectValPath implements TraceObjectValPath {
|
||||
static DBTraceObjectValPath of(Collection<InternalTraceObjectValue> entryList) {
|
||||
return new DBTraceObjectValPath(List.copyOf(entryList));
|
||||
}
|
||||
|
||||
static DBTraceObjectValPath of(InternalTraceObjectValue... entries) {
|
||||
return DBTraceObjectValPath.of(Arrays.asList(entries));
|
||||
}
|
||||
|
||||
private final List<InternalTraceObjectValue> entryList;
|
||||
private List<String> keyList; // lazily computed
|
||||
|
||||
private DBTraceObjectValPath(List<InternalTraceObjectValue> entryList) {
|
||||
this.entryList = entryList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(TraceObjectValPath o) {
|
||||
return PathComparator.KEYED.compare(getKeyList(), o.getKeyList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends InternalTraceObjectValue> getEntryList() {
|
||||
return entryList;
|
||||
}
|
||||
|
||||
protected List<String> computeKeyList() {
|
||||
return entryList.stream()
|
||||
.map(e -> e.getEntryKey())
|
||||
.collect(Collectors.toUnmodifiableList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getKeyList() {
|
||||
if (keyList == null) {
|
||||
keyList = computeKeyList();
|
||||
}
|
||||
return keyList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(TraceObjectValue entry) {
|
||||
return entryList.contains(entry);
|
||||
}
|
||||
|
||||
public DBTraceObjectValPath prepend(TraceObjectValue entry) {
|
||||
InternalTraceObjectValue[] arr = new InternalTraceObjectValue[1 + entryList.size()];
|
||||
arr[0] = (DBTraceObjectValue) entry;
|
||||
for (int i = 1; i < arr.length; i++) {
|
||||
arr[i] = entryList.get(i - 1);
|
||||
}
|
||||
return new DBTraceObjectValPath(Collections.unmodifiableList(Arrays.asList(arr)));
|
||||
}
|
||||
|
||||
public DBTraceObjectValPath append(TraceObjectValue entry) {
|
||||
InternalTraceObjectValue[] arr = new InternalTraceObjectValue[1 + entryList.size()];
|
||||
for (int i = 0; i < arr.length - 1; i++) {
|
||||
arr[i] = entryList.get(i);
|
||||
}
|
||||
arr[arr.length - 1] = (InternalTraceObjectValue) entry;
|
||||
return new DBTraceObjectValPath(Collections.unmodifiableList(Arrays.asList(arr)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public InternalTraceObjectValue getFirstEntry() {
|
||||
if (entryList.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return entryList.get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceObject getFirstParent(TraceObject ifEmpty) {
|
||||
InternalTraceObjectValue first = getFirstEntry();
|
||||
return first == null ? ifEmpty : first.getParent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InternalTraceObjectValue getLastEntry() {
|
||||
if (entryList.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return entryList.get(entryList.size() - 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getLastValue(Object ifEmpty) {
|
||||
InternalTraceObjectValue last = getLastEntry();
|
||||
return last == null ? ifEmpty : last.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceObject getLastChild(TraceObject ifEmpty) {
|
||||
InternalTraceObjectValue last = getLastEntry();
|
||||
return last == null ? ifEmpty : last.getChild();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,420 @@
|
|||
/* ###
|
||||
* 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.trace.database.target;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import db.*;
|
||||
import ghidra.dbg.util.PathPredicates;
|
||||
import ghidra.trace.database.DBTraceUtils;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.trace.model.target.TraceObjectValPath;
|
||||
import ghidra.util.LockHold;
|
||||
import ghidra.util.database.*;
|
||||
import ghidra.util.database.DBCachedObjectStoreFactory.AbstractDBFieldCodec;
|
||||
import ghidra.util.database.DBCachedObjectStoreFactory.VariantDBFieldCodec;
|
||||
import ghidra.util.database.annot.*;
|
||||
|
||||
@DBAnnotatedObjectInfo(version = 0)
|
||||
public class DBTraceObjectValue extends DBAnnotatedObject implements InternalTraceObjectValue {
|
||||
protected static final String TABLE_NAME = "ObjectValue";
|
||||
|
||||
protected static class PrimaryTriple {
|
||||
private final DBTraceObject parent;
|
||||
private final String key;
|
||||
private final long minSnap;
|
||||
|
||||
protected PrimaryTriple(DBTraceObject parent, String key, long minSnap) {
|
||||
this.parent = parent;
|
||||
this.key = key;
|
||||
this.minSnap = minSnap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "<parent=" + parent + ",key=" + key + ",minSnap=" + minSnap + ">";
|
||||
}
|
||||
|
||||
public PrimaryTriple withMinSnap(long minSnap) {
|
||||
return new PrimaryTriple(parent, key, minSnap);
|
||||
}
|
||||
}
|
||||
|
||||
public static class PrimaryTripleDBFieldCodec
|
||||
extends AbstractDBFieldCodec<PrimaryTriple, DBTraceObjectValue, BinaryField> {
|
||||
static final Charset cs = Charset.forName("UTF-8");
|
||||
|
||||
public PrimaryTripleDBFieldCodec(Class<DBTraceObjectValue> objectType, Field field,
|
||||
int column) {
|
||||
super(PrimaryTriple.class, objectType, BinaryField.class, field, column);
|
||||
}
|
||||
|
||||
protected static byte[] encode(PrimaryTriple value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
if (value.key == null) {
|
||||
ByteBuffer buf = ByteBuffer.allocate(Long.BYTES * 2);
|
||||
buf.putLong(DBTraceObjectDBFieldCodec.encode(value.parent) ^ Long.MIN_VALUE);
|
||||
buf.putLong(value.minSnap ^ Long.MIN_VALUE);
|
||||
return buf.array();
|
||||
}
|
||||
|
||||
byte[] keyBytes = value.key.getBytes(cs);
|
||||
ByteBuffer buf = ByteBuffer.allocate(keyBytes.length + 1 + Long.BYTES * 2);
|
||||
|
||||
buf.putLong(DBTraceObjectDBFieldCodec.encode(value.parent) ^ Long.MIN_VALUE);
|
||||
buf.put(keyBytes);
|
||||
buf.put((byte) 0);
|
||||
buf.putLong(value.minSnap ^ Long.MIN_VALUE);
|
||||
|
||||
return buf.array();
|
||||
}
|
||||
|
||||
protected static PrimaryTriple decode(DBTraceObjectValue ent, byte[] enc) {
|
||||
if (enc == null) {
|
||||
return null;
|
||||
}
|
||||
ByteBuffer buf = ByteBuffer.wrap(enc);
|
||||
|
||||
DBTraceObject parent =
|
||||
DBTraceObjectDBFieldCodec.decode(ent, buf.getLong() ^ Long.MIN_VALUE);
|
||||
String key;
|
||||
if (enc.length > Long.BYTES * 2) {
|
||||
int nullPos = ArrayUtils.indexOf(enc, (byte) 0, buf.position());
|
||||
assert nullPos != -1;
|
||||
key = new String(enc, buf.position(), nullPos - buf.position(), cs);
|
||||
buf.position(nullPos + 1);
|
||||
}
|
||||
else {
|
||||
key = null;
|
||||
}
|
||||
long minSnap = buf.getLong() ^ Long.MIN_VALUE;
|
||||
|
||||
return new PrimaryTriple(parent, key, minSnap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void store(PrimaryTriple value, BinaryField f) {
|
||||
f.setBinaryData(encode(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStore(DBTraceObjectValue obj, DBRecord record)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
record.setBinaryData(column, encode(getValue(obj)));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doLoad(DBTraceObjectValue obj, DBRecord record)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
setValue(obj, decode(obj, record.getBinaryData(column)));
|
||||
}
|
||||
}
|
||||
|
||||
public static class DBTraceObjectDBFieldCodec<OV extends DBAnnotatedObject & InternalTraceObjectValue>
|
||||
extends AbstractDBFieldCodec<DBTraceObject, OV, LongField> {
|
||||
public DBTraceObjectDBFieldCodec(Class<OV> objectType, Field field,
|
||||
int column) {
|
||||
super(DBTraceObject.class, objectType, LongField.class, field, column);
|
||||
}
|
||||
|
||||
protected static long encode(DBTraceObject value) {
|
||||
return value == null ? -1 : value.getKey();
|
||||
}
|
||||
|
||||
protected static DBTraceObject decode(InternalTraceObjectValue ent, long enc) {
|
||||
return enc == -1 ? null : ent.getManager().getObjectById(enc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void store(DBTraceObject value, LongField f) {
|
||||
f.setLongValue(encode(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStore(OV obj, DBRecord record)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
record.setLongValue(column, encode(getValue(obj)));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doLoad(OV obj, DBRecord record)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
setValue(obj, decode(obj, record.getLongValue(column)));
|
||||
}
|
||||
}
|
||||
|
||||
static final String TRIPLE_COLUMN_NAME = "Triple";
|
||||
static final String MAX_SNAP_COLUMN_NAME = "MaxSnap";
|
||||
static final String CHILD_COLUMN_NAME = "Child";
|
||||
static final String PRIMITIVE_COLUMN_NAME = "Primitive";
|
||||
|
||||
@DBAnnotatedColumn(TRIPLE_COLUMN_NAME)
|
||||
static DBObjectColumn TRIPLE_COLUMN;
|
||||
@DBAnnotatedColumn(MAX_SNAP_COLUMN_NAME)
|
||||
static DBObjectColumn MAX_SNAP_COLUMN;
|
||||
@DBAnnotatedColumn(CHILD_COLUMN_NAME)
|
||||
static DBObjectColumn CHILD_COLUMN;
|
||||
@DBAnnotatedColumn(PRIMITIVE_COLUMN_NAME)
|
||||
static DBObjectColumn PRIMITIVE_COLUMN;
|
||||
|
||||
@DBAnnotatedField(
|
||||
column = TRIPLE_COLUMN_NAME,
|
||||
indexed = true,
|
||||
codec = PrimaryTripleDBFieldCodec.class)
|
||||
private PrimaryTriple triple;
|
||||
@DBAnnotatedField(column = MAX_SNAP_COLUMN_NAME)
|
||||
private long maxSnap;
|
||||
@DBAnnotatedField(
|
||||
column = CHILD_COLUMN_NAME,
|
||||
indexed = true,
|
||||
codec = DBTraceObjectDBFieldCodec.class)
|
||||
private DBTraceObject child;
|
||||
@DBAnnotatedField(column = PRIMITIVE_COLUMN_NAME, codec = VariantDBFieldCodec.class)
|
||||
private Object primitive;
|
||||
|
||||
protected final DBTraceObjectManager manager;
|
||||
|
||||
private Range<Long> lifespan;
|
||||
|
||||
public DBTraceObjectValue(DBTraceObjectManager manager, DBCachedObjectStore<?> store,
|
||||
DBRecord record) {
|
||||
super(store, record);
|
||||
this.manager = manager;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void fresh(boolean created) throws IOException {
|
||||
if (created) {
|
||||
return;
|
||||
}
|
||||
lifespan = DBTraceUtils.toRange(triple.minSnap, maxSnap);
|
||||
}
|
||||
|
||||
protected void set(Range<Long> lifespan, DBTraceObject parent, String key, Object value) {
|
||||
this.triple = new PrimaryTriple(parent, key, DBTraceUtils.lowerEndpoint(lifespan));
|
||||
this.maxSnap = DBTraceUtils.upperEndpoint(lifespan);
|
||||
this.lifespan = DBTraceUtils.toRange(triple.minSnap, maxSnap);
|
||||
if (value instanceof TraceObject) {
|
||||
DBTraceObject child = manager.assertIsMine((TraceObject) value);
|
||||
this.child = child;
|
||||
this.primitive = null;
|
||||
}
|
||||
else {
|
||||
this.primitive = manager.validatePrimitive(value);
|
||||
this.child = null;
|
||||
}
|
||||
update(TRIPLE_COLUMN, MAX_SNAP_COLUMN, CHILD_COLUMN, PRIMITIVE_COLUMN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + ": parent=" + triple.parent + ", key=" + triple.key +
|
||||
", lifespan=" + getLifespan() + ", value=" + getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doSetLifespan(Range<Long> lifespan) {
|
||||
long minSnap = DBTraceUtils.lowerEndpoint(lifespan);
|
||||
if (this.triple.minSnap != minSnap) {
|
||||
this.triple = triple.withMinSnap(minSnap);
|
||||
update(TRIPLE_COLUMN);
|
||||
}
|
||||
this.maxSnap = DBTraceUtils.upperEndpoint(lifespan);
|
||||
update(MAX_SNAP_COLUMN);
|
||||
this.lifespan = DBTraceUtils.toRange(minSnap, maxSnap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Trace getTrace() {
|
||||
return manager.trace;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBTraceObjectManager getManager() {
|
||||
return manager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBTraceObject getParent() {
|
||||
return triple.parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEntryKey() {
|
||||
return triple.key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
try (LockHold hold = manager.trace.lockRead()) {
|
||||
return child != null ? child : primitive;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBTraceObject getChild() {
|
||||
return (DBTraceObject) getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Range<Long> getLifespan() {
|
||||
try (LockHold hold = manager.trace.lockRead()) {
|
||||
return lifespan;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMinSnap(long minSnap) {
|
||||
try (LockHold hold = manager.trace.lockWrite()) {
|
||||
setLifespan(DBTraceUtils.toRange(minSnap, maxSnap));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMinSnap() {
|
||||
try (LockHold hold = manager.trace.lockRead()) {
|
||||
return triple.minSnap;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxSnap(long maxSnap) {
|
||||
try (LockHold hold = manager.trace.lockWrite()) {
|
||||
setLifespan(DBTraceUtils.toRange(triple.minSnap, maxSnap));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMaxSnap() {
|
||||
try (LockHold hold = manager.trace.lockRead()) {
|
||||
return maxSnap;
|
||||
}
|
||||
}
|
||||
|
||||
protected Stream<TraceObjectValPath> doGetAllPaths(Range<Long> span,
|
||||
DBTraceObjectValPath post) {
|
||||
return triple.parent.doGetAllPaths(span, post.prepend(this));
|
||||
}
|
||||
|
||||
protected Stream<? extends DBTraceObjectValPath> doGetAncestors(Range<Long> span,
|
||||
DBTraceObjectValPath post, PathPredicates predicates) {
|
||||
return triple.parent.doGetAncestors(span, post.prepend(this), predicates);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<? extends DBTraceObjectValPath> doGetSuccessors(
|
||||
Range<Long> span, DBTraceObjectValPath pre, PathPredicates predicates) {
|
||||
DBTraceObjectValPath path = pre == null ? DBTraceObjectValPath.of() : pre.append(this);
|
||||
boolean includeMe = predicates.matches(path.getKeyList());
|
||||
boolean descend = child != null;
|
||||
if (includeMe && descend) {
|
||||
return Stream.concat(Stream.of(path), child.doGetSuccessors(span, path, predicates));
|
||||
}
|
||||
if (includeMe) {
|
||||
return Stream.of(path);
|
||||
}
|
||||
if (descend) {
|
||||
return child.doGetSuccessors(span, path, predicates);
|
||||
}
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<? extends DBTraceObjectValPath> doGetOrderedSuccessors(Range<Long> span,
|
||||
DBTraceObjectValPath pre, PathPredicates predicates, boolean forward) {
|
||||
DBTraceObjectValPath path = pre == null ? DBTraceObjectValPath.of() : pre.append(this);
|
||||
if (predicates.matches(path.getKeyList())) {
|
||||
// Singleton path, so if I match, nothing below can
|
||||
return Stream.of(path);
|
||||
}
|
||||
if (child == null) {
|
||||
return Stream.of();
|
||||
}
|
||||
return child.doGetOrderedSuccessors(span, path, predicates, forward);
|
||||
}
|
||||
|
||||
protected boolean doIsCanonical() {
|
||||
if (child == null) {
|
||||
return false;
|
||||
}
|
||||
if (triple.parent == null) {
|
||||
return true;
|
||||
}
|
||||
return triple.parent.getCanonicalPath().extend(triple.key).equals(child.getCanonicalPath());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCanonical() {
|
||||
try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
|
||||
return doIsCanonical();
|
||||
}
|
||||
}
|
||||
|
||||
protected void doDeleteSuccessors() {
|
||||
if (!doIsCanonical()) {
|
||||
return;
|
||||
}
|
||||
child.doDeleteTree();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doDelete() {
|
||||
manager.doDeleteEdge(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
try (LockHold hold = LockHold.lock(manager.lock.writeLock())) {
|
||||
if (triple.parent == null) {
|
||||
throw new IllegalArgumentException("Cannot delete root value");
|
||||
}
|
||||
doDelete();
|
||||
}
|
||||
}
|
||||
|
||||
protected void doDeleteTree() {
|
||||
doDeleteSuccessors();
|
||||
doDelete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteTree() {
|
||||
try (LockHold hold = LockHold.lock(manager.lock.writeLock())) {
|
||||
doDeleteTree();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InternalTraceObjectValue truncateOrDelete(Range<Long> span) {
|
||||
try (LockHold hold = LockHold.lock(manager.lock.writeLock())) {
|
||||
if (triple.parent == null) {
|
||||
throw new IllegalArgumentException("Cannot truncate or delete root value");
|
||||
}
|
||||
return doTruncateOrDelete(span);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,173 @@
|
|||
/* ###
|
||||
* 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.trace.database.target;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.commons.collections4.IterableUtils;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import ghidra.dbg.util.PathPredicates;
|
||||
import ghidra.trace.database.DBTraceUtils;
|
||||
import ghidra.trace.database.DBTraceUtils.LifespanMapSetter;
|
||||
import ghidra.trace.model.Trace.TraceObjectChangeType;
|
||||
import ghidra.trace.model.target.TraceObject.ConflictResolution;
|
||||
import ghidra.trace.model.target.TraceObjectValue;
|
||||
import ghidra.trace.util.TraceChangeRecord;
|
||||
import ghidra.util.LockHold;
|
||||
|
||||
interface InternalTraceObjectValue extends TraceObjectValue {
|
||||
abstract class ValueLifespanSetter
|
||||
extends LifespanMapSetter<InternalTraceObjectValue, Object> {
|
||||
protected final Range<Long> range;
|
||||
protected final Object value;
|
||||
protected InternalTraceObjectValue keep = null;
|
||||
protected Collection<InternalTraceObjectValue> kept = new ArrayList<>(2);
|
||||
|
||||
public ValueLifespanSetter(Range<Long> range, Object value) {
|
||||
this.range = range;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public ValueLifespanSetter(Range<Long> range, Object value,
|
||||
InternalTraceObjectValue keep) {
|
||||
this(range, value);
|
||||
this.keep = keep;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Range<Long> getRange(InternalTraceObjectValue entry) {
|
||||
return entry.getLifespan();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object getValue(InternalTraceObjectValue entry) {
|
||||
return entry.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void remove(InternalTraceObjectValue entry) {
|
||||
if (Objects.equals(entry.getValue(), value)) {
|
||||
if (keep == null) {
|
||||
keep = entry;
|
||||
}
|
||||
else {
|
||||
entry.doDelete();
|
||||
}
|
||||
}
|
||||
else {
|
||||
entry.doTruncateOrDelete(range);
|
||||
if (!entry.isDeleted()) {
|
||||
kept.add(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InternalTraceObjectValue put(Range<Long> range, Object value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
if (keep != null && Objects.equals(this.value, value)) {
|
||||
keep.doSetLifespan(range);
|
||||
return keep;
|
||||
}
|
||||
for (InternalTraceObjectValue k : kept) {
|
||||
if (Objects.equals(value, k.getValue()) && Objects.equals(range, k.getLifespan())) {
|
||||
kept.remove(k);
|
||||
return k;
|
||||
}
|
||||
}
|
||||
return create(range, value);
|
||||
}
|
||||
|
||||
protected abstract InternalTraceObjectValue create(Range<Long> range, Object value);
|
||||
}
|
||||
|
||||
DBTraceObjectManager getManager();
|
||||
|
||||
/**
|
||||
* Get the database key
|
||||
*
|
||||
* @return the key
|
||||
*/
|
||||
long getKey();
|
||||
|
||||
@Override
|
||||
DBTraceObject getChild();
|
||||
|
||||
void doSetLifespan(Range<Long> range);
|
||||
|
||||
@Override
|
||||
default void setLifespan(Range<Long> lifespan) {
|
||||
setLifespan(lifespan, ConflictResolution.TRUNCATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
default void setLifespan(Range<Long> lifespan, ConflictResolution resolution) {
|
||||
try (LockHold hold = getTrace().lockWrite()) {
|
||||
Range<Long> oldLifespan = getLifespan();
|
||||
if (getParent() == null) {
|
||||
throw new IllegalArgumentException("Cannot set lifespan of root value");
|
||||
}
|
||||
if (resolution == ConflictResolution.DENY) {
|
||||
getParent().doCheckConflicts(lifespan, getEntryKey(), getValue());
|
||||
}
|
||||
new ValueLifespanSetter(lifespan, getValue(), this) {
|
||||
@Override
|
||||
protected Iterable<InternalTraceObjectValue> getIntersecting(Long lower,
|
||||
Long upper) {
|
||||
Collection<InternalTraceObjectValue> col = Collections.unmodifiableCollection(
|
||||
getParent().doGetValues(lower, upper, getEntryKey()));
|
||||
return IterableUtils.filteredIterable(col, v -> v != keep);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InternalTraceObjectValue create(Range<Long> range, Object value) {
|
||||
return getParent().doCreateValue(range, getEntryKey(), value);
|
||||
}
|
||||
}.set(lifespan, getValue());
|
||||
getParent().emitEvents(new TraceChangeRecord<>(
|
||||
TraceObjectChangeType.VALUE_LIFESPAN_CHANGED, null, this, oldLifespan, lifespan));
|
||||
}
|
||||
}
|
||||
|
||||
Stream<? extends DBTraceObjectValPath> doGetSuccessors(Range<Long> span,
|
||||
DBTraceObjectValPath pre, PathPredicates predicates);
|
||||
|
||||
Stream<? extends DBTraceObjectValPath> doGetOrderedSuccessors(Range<Long> span,
|
||||
DBTraceObjectValPath pre, PathPredicates predicates, boolean forward);
|
||||
|
||||
void doDelete();
|
||||
|
||||
@Override
|
||||
DBTraceObject getParent();
|
||||
|
||||
default InternalTraceObjectValue doTruncateOrDelete(Range<Long> span) {
|
||||
List<Range<Long>> removed = DBTraceUtils.subtract(getLifespan(), span);
|
||||
if (removed.isEmpty()) {
|
||||
doDelete();
|
||||
return null;
|
||||
}
|
||||
doSetLifespan(removed.get(0));
|
||||
if (removed.size() == 2) {
|
||||
return getParent().doCreateValue(removed.get(1), getEntryKey(), getValue());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,222 @@
|
|||
/* ###
|
||||
* 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.trace.database.target;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import ghidra.lifecycle.Experimental;
|
||||
import ghidra.trace.database.DBTraceUtils;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.trace.model.target.TraceObject.ConflictResolution;
|
||||
import ghidra.trace.model.target.TraceObjectValue;
|
||||
|
||||
@Experimental
|
||||
public class LifespanCorrector {
|
||||
/**
|
||||
* A visitor for lifespan correction
|
||||
*
|
||||
* <p>
|
||||
* Implementors must implement only the upward pair, or only the downward pair
|
||||
*/
|
||||
public interface Visitor {
|
||||
/**
|
||||
* Visit an object on the upward side of traversal
|
||||
*
|
||||
* @param object the object
|
||||
*/
|
||||
default void visitObjectUpward(TraceObject object) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Visit an object on the downward side of traversal
|
||||
*
|
||||
* @param object the object
|
||||
*/
|
||||
default void visitObjectDownward(TraceObject object) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Visit a value on the upward side of traversal
|
||||
*
|
||||
* @param value the value, guaranteed to have a child
|
||||
*/
|
||||
default void visitValueUpward(TraceObjectValue value) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Visit a value on the downward side of traversal
|
||||
*
|
||||
* @param value the value, guaranteed to have a child
|
||||
*/
|
||||
default void visitValueDownward(TraceObjectValue value) {
|
||||
}
|
||||
}
|
||||
|
||||
public enum Direction {
|
||||
ANCESTORS {
|
||||
@Override
|
||||
public void visit(TraceObject seed, Visitor visitor) {
|
||||
visitObjectAncestors(seed, visitor, UP | DOWN);
|
||||
}
|
||||
|
||||
},
|
||||
SUCCESSORS {
|
||||
@Override
|
||||
public void visit(TraceObject seed, Visitor visitor) {
|
||||
visitObjectSuccessors(seed, visitor, true);
|
||||
}
|
||||
},
|
||||
BOTH {
|
||||
@Override
|
||||
public void visit(TraceObject seed, Visitor visitor) {
|
||||
visitObjectAncestors(seed, visitor, DOWN);
|
||||
visitObjectSuccessors(seed, visitor, false);
|
||||
visitObjectAncestors(seed, visitor, UP);
|
||||
}
|
||||
};
|
||||
|
||||
static final int UP = 1;
|
||||
static final int DOWN = 2;
|
||||
|
||||
public abstract void visit(TraceObject seed, Visitor visitor);
|
||||
|
||||
void visitObjectAncestors(TraceObject object, Visitor visitor, int mode) {
|
||||
if ((mode & UP) == UP) {
|
||||
visitor.visitObjectUpward(object);
|
||||
}
|
||||
if (!object.isRoot()) {
|
||||
for (TraceObjectValue value : object.getParents()) {
|
||||
// Yes, over time, there may be multiple canonical values
|
||||
if (value.isCanonical() && !value.isDeleted()) {
|
||||
visitValueAncestors(value, visitor, mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((mode & DOWN) == DOWN) {
|
||||
visitor.visitObjectDownward(object);
|
||||
}
|
||||
}
|
||||
|
||||
void visitValueAncestors(TraceObjectValue value, Visitor visitor, int mode) {
|
||||
visitor.visitValueUpward(value);
|
||||
visitObjectAncestors(value.getParent(), visitor, mode);
|
||||
visitor.visitValueDownward(value);
|
||||
}
|
||||
|
||||
void visitObjectSuccessors(TraceObject object, Visitor visitor, boolean includeCur) {
|
||||
if (includeCur) {
|
||||
visitor.visitObjectDownward(object);
|
||||
}
|
||||
for (TraceObjectValue value : object.getValues()) {
|
||||
if (value.isCanonical() && !value.isDeleted()) {
|
||||
visitValueSuccessors(value, visitor);
|
||||
}
|
||||
}
|
||||
if (includeCur) {
|
||||
visitor.visitObjectUpward(object);
|
||||
}
|
||||
}
|
||||
|
||||
void visitValueSuccessors(TraceObjectValue value, Visitor visitor) {
|
||||
if (!(value.getValue() instanceof TraceObject)) {
|
||||
return;
|
||||
}
|
||||
visitor.visitValueDownward(value);
|
||||
visitObjectSuccessors(value.getChild(), visitor, true);
|
||||
visitor.visitValueUpward(value);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Consider non-canonical paths?
|
||||
|
||||
public enum Operation {
|
||||
EXPAND {
|
||||
@Override
|
||||
Visitor getVisitor(ConflictResolution resolution) {
|
||||
return new Visitor() {
|
||||
@Override
|
||||
public void visitObjectUpward(TraceObject object) {
|
||||
Range<Long> span = object.getLifespan();
|
||||
for (TraceObjectValue value : object.getValues()) {
|
||||
span = span.span(value.getLifespan());
|
||||
}
|
||||
object.setLifespan(span);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitValueUpward(TraceObjectValue value) {
|
||||
Range<Long> newLife =
|
||||
value.getLifespan().span(value.getChild().getLifespan());
|
||||
value.setLifespan(newLife, resolution);
|
||||
}
|
||||
};
|
||||
}
|
||||
},
|
||||
SHRINK {
|
||||
@Override
|
||||
Visitor getVisitor(ConflictResolution resolution) {
|
||||
return new Visitor() {
|
||||
@Override
|
||||
public void visitObjectDownward(TraceObject object) {
|
||||
for (TraceObjectValue value : object.getValues()) {
|
||||
if (!DBTraceUtils.intersect(object.getLifespan(),
|
||||
value.getLifespan())) {
|
||||
value.delete();
|
||||
continue;
|
||||
}
|
||||
value.setLifespan(
|
||||
value.getLifespan().intersection(object.getLifespan()), resolution);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitValueDownward(TraceObjectValue value) {
|
||||
/**
|
||||
* It'd be an odd circumstance for two canonical entries to exist for the
|
||||
* same parent and child. If that happens, this will cause the child to
|
||||
* become detached, since those entries cannot intersect.
|
||||
*/
|
||||
if (!DBTraceUtils.intersect(value.getLifespan(),
|
||||
value.getChild().getLifespan())) {
|
||||
value.getChild().delete();
|
||||
return;
|
||||
}
|
||||
Range<Long> newLife =
|
||||
value.getLifespan().intersection(value.getChild().getLifespan());
|
||||
value.getChild().setLifespan(newLife);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
abstract Visitor getVisitor(ConflictResolution resolution);
|
||||
}
|
||||
|
||||
private final Direction direction;
|
||||
private final Operation operation;
|
||||
private final ConflictResolution resolution;
|
||||
|
||||
public LifespanCorrector(Direction direction, Operation operation,
|
||||
ConflictResolution resolution) {
|
||||
this.direction = direction;
|
||||
this.operation = operation;
|
||||
this.resolution = resolution;
|
||||
}
|
||||
|
||||
public void correctLifespans(TraceObject seed) {
|
||||
direction.visit(seed, operation.getVisitor(resolution));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,166 @@
|
|||
/* ###
|
||||
* 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.trace.database.thread;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.trace.database.DBTraceUtils;
|
||||
import ghidra.trace.database.target.DBTraceObject;
|
||||
import ghidra.trace.database.target.DBTraceObjectInterface;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.Trace.TraceThreadChangeType;
|
||||
import ghidra.trace.model.target.annot.TraceObjectInterfaceUtils;
|
||||
import ghidra.trace.model.thread.TraceObjectThread;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.util.TraceChangeRecord;
|
||||
import ghidra.trace.util.TraceChangeType;
|
||||
import ghidra.util.LockHold;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
public class DBTraceObjectThread implements TraceObjectThread, DBTraceObjectInterface {
|
||||
|
||||
protected class ThreadChangeTranslator extends Translator<TraceThread> {
|
||||
protected ThreadChangeTranslator(DBTraceObject object, TraceThread iface) {
|
||||
super(null, object, iface);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TraceChangeType<TraceThread, Void> getAddedType() {
|
||||
return TraceThreadChangeType.ADDED;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TraceChangeType<TraceThread, Range<Long>> getLifespanChangedType() {
|
||||
return TraceThreadChangeType.LIFESPAN_CHANGED;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TraceChangeType<TraceThread, Void> getChangedType() {
|
||||
return TraceThreadChangeType.CHANGED;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean appliesToKey(String key) {
|
||||
return KEY_COMMENT.equals(key) ||
|
||||
TargetObject.DISPLAY_ATTRIBUTE_NAME.equals(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TraceChangeType<TraceThread, Void> getDeletedType() {
|
||||
return TraceThreadChangeType.DELETED;
|
||||
}
|
||||
}
|
||||
|
||||
private final DBTraceObject object;
|
||||
private final ThreadChangeTranslator translator;
|
||||
|
||||
public DBTraceObjectThread(DBTraceObject object) {
|
||||
this.object = object;
|
||||
|
||||
translator = new ThreadChangeTranslator(object, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBTraceObject getObject() {
|
||||
return object;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Trace getTrace() {
|
||||
return object.getTrace();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getKey() {
|
||||
return object.getKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
return object.getCanonicalPath().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return TraceObjectInterfaceUtils.getValue(object, getCreationSnap(),
|
||||
TargetObject.DISPLAY_ATTRIBUTE_NAME, String.class, "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(String name) {
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
object.setValue(getLifespan(), TargetObject.DISPLAY_ATTRIBUTE_NAME, name);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCreationSnap(long creationSnap) throws DuplicateNameException {
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
setLifespan(DBTraceUtils.toRange(creationSnap, getDestructionSnap()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getCreationSnap() {
|
||||
return object.getMinSnap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDestructionSnap(long destructionSnap) throws DuplicateNameException {
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
setLifespan(DBTraceUtils.toRange(getCreationSnap(), destructionSnap));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDestructionSnap() {
|
||||
return object.getMaxSnap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLifespan(Range<Long> lifespan) throws DuplicateNameException {
|
||||
TraceObjectInterfaceUtils.setLifespan(TraceObjectThread.class, object, lifespan);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Range<Long> getLifespan() {
|
||||
return object.getLifespan();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setComment(String comment) {
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
object.setValue(getLifespan(), KEY_COMMENT, comment);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getComment() {
|
||||
return TraceObjectInterfaceUtils.getValue(object, getCreationSnap(), KEY_COMMENT,
|
||||
String.class, "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
object.deleteTree();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceChangeRecord<?, ?> translateEvent(TraceChangeRecord<?, ?> rec) {
|
||||
return translator.translate(rec);
|
||||
}
|
||||
}
|
|
@ -18,14 +18,15 @@ package ghidra.trace.database.thread;
|
|||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import db.DBHandle;
|
||||
import ghidra.trace.database.*;
|
||||
import ghidra.trace.database.target.DBTraceObjectManager;
|
||||
import ghidra.trace.model.Trace.TraceThreadChangeType;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.model.thread.TraceThreadManager;
|
||||
import ghidra.trace.model.thread.*;
|
||||
import ghidra.trace.util.TraceChangeRecord;
|
||||
import ghidra.util.LockHold;
|
||||
import ghidra.util.database.*;
|
||||
|
@ -37,14 +38,19 @@ public class DBTraceThreadManager implements TraceThreadManager, DBTraceManager
|
|||
protected final ReadWriteLock lock;
|
||||
protected final DBTrace trace;
|
||||
|
||||
protected final DBTraceObjectManager objectManager;
|
||||
|
||||
protected final DBCachedObjectStore<DBTraceThread> threadStore;
|
||||
protected final DBCachedObjectIndex<String, DBTraceThread> threadsByPath;
|
||||
|
||||
public DBTraceThreadManager(DBHandle dbh, DBOpenMode openMode, ReadWriteLock lock,
|
||||
TaskMonitor monitor, DBTrace trace) throws IOException, VersionException {
|
||||
TaskMonitor monitor, DBTrace trace, DBTraceObjectManager objectManager)
|
||||
throws IOException, VersionException {
|
||||
this.lock = lock;
|
||||
this.trace = trace;
|
||||
|
||||
this.objectManager = objectManager;
|
||||
|
||||
DBCachedObjectStoreFactory factory = trace.getStoreFactory();
|
||||
|
||||
threadStore = factory.getOrCreateCachedStore(DBTraceThread.TABLE_NAME, DBTraceThread.class,
|
||||
|
@ -63,7 +69,13 @@ public class DBTraceThreadManager implements TraceThreadManager, DBTraceManager
|
|||
}
|
||||
|
||||
// Internal
|
||||
public DBTraceThread assertIsMine(TraceThread thread) {
|
||||
public TraceThread assertIsMine(TraceThread thread) {
|
||||
if (thread == null) {
|
||||
return null;
|
||||
}
|
||||
if (objectManager.hasSchema()) {
|
||||
return objectManager.assertMyThread(thread);
|
||||
}
|
||||
if (!(thread instanceof DBTraceThread)) {
|
||||
throw new IllegalArgumentException("Thread " + thread + " is not part of this trace");
|
||||
}
|
||||
|
@ -92,14 +104,17 @@ public class DBTraceThreadManager implements TraceThreadManager, DBTraceManager
|
|||
}
|
||||
|
||||
@Override
|
||||
public DBTraceThread addThread(String path, Range<Long> lifespan)
|
||||
public TraceThread addThread(String path, Range<Long> lifespan)
|
||||
throws DuplicateNameException {
|
||||
return addThread(path, path, lifespan);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBTraceThread addThread(String path, String display, Range<Long> lifespan)
|
||||
public TraceThread addThread(String path, String display, Range<Long> lifespan)
|
||||
throws DuplicateNameException {
|
||||
if (objectManager.hasSchema()) {
|
||||
return objectManager.addThread(path, display, lifespan);
|
||||
}
|
||||
DBTraceThread thread;
|
||||
try (LockHold hold = LockHold.lock(lock.writeLock())) {
|
||||
checkConflictingPath(null, path, lifespan);
|
||||
|
@ -111,17 +126,26 @@ public class DBTraceThreadManager implements TraceThreadManager, DBTraceManager
|
|||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends DBTraceThread> getAllThreads() {
|
||||
public Collection<? extends TraceThread> getAllThreads() {
|
||||
if (objectManager.hasSchema()) {
|
||||
return objectManager.getAllObjects(TraceObjectThread.class);
|
||||
}
|
||||
return Collections.unmodifiableCollection(threadStore.asMap().values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends DBTraceThread> getThreadsByPath(String path) {
|
||||
public Collection<? extends TraceThread> getThreadsByPath(String path) {
|
||||
if (objectManager.hasSchema()) {
|
||||
return objectManager.getObjectsByPath(path, TraceObjectThread.class);
|
||||
}
|
||||
return Collections.unmodifiableCollection(threadsByPath.get(path));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBTraceThread getLiveThreadByPath(long snap, String path) {
|
||||
public TraceThread getLiveThreadByPath(long snap, String path) {
|
||||
if (objectManager.hasSchema()) {
|
||||
return objectManager.getObjectByPath(snap, path, TraceObjectThread.class);
|
||||
}
|
||||
try (LockHold hold = LockHold.lock(lock.readLock())) {
|
||||
return threadsByPath.get(path)
|
||||
.stream()
|
||||
|
@ -132,16 +156,32 @@ public class DBTraceThreadManager implements TraceThreadManager, DBTraceManager
|
|||
}
|
||||
|
||||
@Override
|
||||
public DBTraceThread getThread(long key) {
|
||||
public TraceThread getThread(long key) {
|
||||
if (objectManager.hasSchema()) {
|
||||
return objectManager
|
||||
.getObjectById(key)
|
||||
.queryInterface(TraceObjectThread.class);
|
||||
}
|
||||
return threadStore.getObjectAt(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends DBTraceThread> getLiveThreads(long snap) {
|
||||
public Collection<? extends TraceThread> getLiveThreads(long snap) {
|
||||
if (objectManager.hasSchema()) {
|
||||
try (LockHold hold = LockHold.lock(lock.readLock())) {
|
||||
return objectManager
|
||||
.queryAllInterface(Range.singleton(snap), TraceObjectThread.class)
|
||||
// Exclude the destruction
|
||||
.filter(thread -> thread.getCreationSnap() <= snap &&
|
||||
snap < thread.getDestructionSnap())
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
try (LockHold hold = LockHold.lock(lock.readLock())) {
|
||||
// NOTE: Should be few enough threads that this is fast
|
||||
Collection<DBTraceThread> result = new LinkedHashSet<>();
|
||||
for (DBTraceThread thread : threadStore.asMap().values()) {
|
||||
// Don't use .getLifespan().contains(snap). Exclude the destruction.
|
||||
if (thread.getCreationSnap() <= snap && snap < thread.getDestructionSnap()) {
|
||||
result.add(thread);
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ package ghidra.trace.database.time;
|
|||
import java.io.IOException;
|
||||
|
||||
import db.DBRecord;
|
||||
import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.Trace.TraceSnapshotChangeType;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
|
@ -59,7 +58,7 @@ public class DBTraceSnapshot extends DBAnnotatedObject implements TraceSnapshot
|
|||
|
||||
public final DBTraceTimeManager manager;
|
||||
|
||||
private DBTraceThread eventThread;
|
||||
private TraceThread eventThread;
|
||||
private TraceSchedule schedule;
|
||||
|
||||
public DBTraceSnapshot(DBTraceTimeManager manager, DBCachedObjectStore<?> store,
|
||||
|
|
|
@ -73,6 +73,12 @@ public class ImmutableTraceAddressSnapRange implements TraceAddressSnapRange {
|
|||
this.space = TraceAddressSnapSpace.forAddressSpace(range.getAddressSpace());
|
||||
}
|
||||
|
||||
public ImmutableTraceAddressSnapRange(AddressRange range, long snap) {
|
||||
this.range = range;
|
||||
this.lifespan = DBTraceUtils.toRange(snap, snap);
|
||||
this.space = TraceAddressSnapSpace.forAddressSpace(range.getAddressSpace());
|
||||
}
|
||||
|
||||
public ImmutableTraceAddressSnapRange(Address minAddress, Address maxAddress,
|
||||
Range<Long> lifespan, EuclideanSpace2D<Address, Long> space) {
|
||||
this.range = new AddressRangeImpl(minAddress, maxAddress);
|
||||
|
|
|
@ -41,6 +41,7 @@ import ghidra.trace.model.program.TraceVariableSnapProgramView;
|
|||
import ghidra.trace.model.stack.TraceStack;
|
||||
import ghidra.trace.model.stack.TraceStackManager;
|
||||
import ghidra.trace.model.symbol.*;
|
||||
import ghidra.trace.model.target.*;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.model.thread.TraceThreadManager;
|
||||
import ghidra.trace.model.time.TraceSnapshot;
|
||||
|
@ -53,6 +54,47 @@ import resources.ResourceManager;
|
|||
public interface Trace extends DataTypeManagerDomainObject {
|
||||
ImageIcon TRACE_ICON = ResourceManager.loadImage("images/video-x-generic16.png");
|
||||
|
||||
public static final class TraceObjectChangeType<T, U>
|
||||
extends DefaultTraceChangeType<T, U> {
|
||||
/**
|
||||
* An object was created, but not necessarily inserted.
|
||||
*/
|
||||
public static final TraceObjectChangeType<TraceObject, Void> CREATED =
|
||||
new TraceObjectChangeType<>();
|
||||
/**
|
||||
* An object's lifespan changed.
|
||||
*/
|
||||
public static final TraceObjectChangeType<TraceObject, Range<Long>> LIFESPAN_CHANGED =
|
||||
new TraceObjectChangeType<>();
|
||||
/**
|
||||
* An object was deleted.
|
||||
*/
|
||||
public static final TraceObjectChangeType<TraceObject, Void> DELETED =
|
||||
new TraceObjectChangeType<>();
|
||||
/**
|
||||
* An object's value changed.
|
||||
*
|
||||
* <p>
|
||||
* If the old value is uniform for the new value's lifespan, that value is passed as the old
|
||||
* value. Otherwise, {@code null} is passed for the old value. If the value was cleared,
|
||||
* {@code null} is passed for the new value.
|
||||
*/
|
||||
public static final TraceObjectChangeType<TraceObjectValue, Object> VALUE_CHANGED =
|
||||
new TraceObjectChangeType<>();
|
||||
/**
|
||||
* An object's value changed in lifespan.
|
||||
*
|
||||
* <p>
|
||||
* This is only called for the value on which {@link TraceObjectValue#setLifespan(Range)} or
|
||||
* similar is called. If other values are truncated or deleted, there is no event. Listeners
|
||||
* concerned about a single snap need only check if the snap is contained in the new and old
|
||||
* lifespans. Listeners concerned about the full timeline can refresh the parent object's
|
||||
* values, or compute the coalescing and truncation manually.
|
||||
*/
|
||||
public static final TraceObjectChangeType<TraceObjectValue, Range<Long>> //
|
||||
VALUE_LIFESPAN_CHANGED = new TraceObjectChangeType<>();
|
||||
}
|
||||
|
||||
public static final class TraceBookmarkChangeType<T, U> extends DefaultTraceChangeType<T, U> {
|
||||
public static final TraceBookmarkChangeType<TraceBookmarkType, Void> TYPE_ADDED =
|
||||
new TraceBookmarkChangeType<>();
|
||||
|
@ -350,6 +392,8 @@ public interface Trace extends DataTypeManagerDomainObject {
|
|||
|
||||
TraceModuleManager getModuleManager();
|
||||
|
||||
TraceObjectManager getObjectManager();
|
||||
|
||||
TraceReferenceManager getReferenceManager();
|
||||
|
||||
TraceRegisterContextManager getRegisterContextManager();
|
||||
|
|
|
@ -17,11 +17,18 @@ package ghidra.trace.model;
|
|||
|
||||
import ghidra.util.database.ObjectKey;
|
||||
|
||||
public interface TraceObject {
|
||||
public interface TraceUniqueObject {
|
||||
/**
|
||||
* Get an opaque unique id for this object, whose hash is immutable
|
||||
*
|
||||
* @return the opaque object id
|
||||
*/
|
||||
ObjectKey getObjectKey();
|
||||
|
||||
/**
|
||||
* Check if this object is deleted
|
||||
*
|
||||
* @return true if deleted
|
||||
*/
|
||||
boolean isDeleted();
|
||||
}
|
|
@ -23,14 +23,14 @@ import com.google.common.collect.Range;
|
|||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.TraceObject;
|
||||
import ghidra.trace.model.TraceUniqueObject;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
/**
|
||||
* A breakpoint in a trace
|
||||
*/
|
||||
public interface TraceBreakpoint extends TraceObject {
|
||||
public interface TraceBreakpoint extends TraceUniqueObject {
|
||||
|
||||
/**
|
||||
* Get the trace containing this breakpoint
|
||||
|
@ -162,11 +162,11 @@ public interface TraceBreakpoint extends TraceObject {
|
|||
void setEnabled(boolean enabled);
|
||||
|
||||
/**
|
||||
* Check whether this breakpoint is enabled or disabled
|
||||
* Check whether this breakpoint is enabled or disabled at the given snap
|
||||
*
|
||||
* @return true if enabled, false if disabled
|
||||
*/
|
||||
boolean isEnabled();
|
||||
boolean isEnabled(long snap);
|
||||
|
||||
/**
|
||||
* Set the kinds included in this breakpoint
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
/* ###
|
||||
* 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.trace.model.breakpoint;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import ghidra.dbg.target.TargetBreakpointLocation;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.trace.model.target.TraceObjectInterface;
|
||||
import ghidra.trace.model.target.annot.TraceObjectInfo;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
@TraceObjectInfo(
|
||||
targetIf = TargetBreakpointLocation.class,
|
||||
shortName = "breakpoint location",
|
||||
fixedKeys = {
|
||||
TargetObject.DISPLAY_ATTRIBUTE_NAME,
|
||||
TargetBreakpointLocation.ADDRESS_ATTRIBUTE_NAME,
|
||||
TargetBreakpointLocation.LENGTH_ATTRIBUTE_NAME,
|
||||
TraceObjectBreakpointLocation.KEY_COMMENT,
|
||||
TraceObjectBreakpointLocation.KEY_RANGE,
|
||||
})
|
||||
public interface TraceObjectBreakpointLocation extends TraceBreakpoint, TraceObjectInterface {
|
||||
String KEY_COMMENT = "_comment";
|
||||
String KEY_RANGE = "_range"; // Duplicates address,length
|
||||
|
||||
TraceObjectBreakpointSpec getSpecification();
|
||||
|
||||
void setLifespan(Range<Long> lifespan) throws DuplicateNameException;
|
||||
|
||||
void setRange(AddressRange range);
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/* ###
|
||||
* 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.trace.model.breakpoint;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import ghidra.dbg.target.TargetBreakpointSpec;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.trace.model.target.TraceObjectInterface;
|
||||
import ghidra.trace.model.target.annot.TraceObjectInfo;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
/**
|
||||
* TODO:
|
||||
*
|
||||
* <p>
|
||||
* NOTE: When enumerating trace breakpoints, use the locations, not the specifications.
|
||||
*/
|
||||
@TraceObjectInfo(
|
||||
targetIf = TargetBreakpointSpec.class,
|
||||
shortName = "breakpoint specification",
|
||||
fixedKeys = {
|
||||
TargetObject.DISPLAY_ATTRIBUTE_NAME,
|
||||
TargetBreakpointSpec.EXPRESSION_ATTRIBUTE_NAME,
|
||||
TargetBreakpointSpec.KINDS_ATTRIBUTE_NAME,
|
||||
})
|
||||
public interface TraceObjectBreakpointSpec extends TraceBreakpoint, TraceObjectInterface {
|
||||
void setLifespan(Range<Long> lifespan) throws DuplicateNameException;
|
||||
|
||||
Collection<? extends TraceObjectBreakpointLocation> getLocations();
|
||||
}
|
|
@ -15,6 +15,9 @@
|
|||
*/
|
||||
package ghidra.trace.model.memory;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.EnumSet;
|
||||
|
||||
import ghidra.program.model.mem.MemoryBlock;
|
||||
|
||||
public enum TraceMemoryFlag {
|
||||
|
@ -23,6 +26,27 @@ public enum TraceMemoryFlag {
|
|||
READ(MemoryBlock.READ),
|
||||
VOLATILE(MemoryBlock.VOLATILE);
|
||||
|
||||
public static EnumSet<TraceMemoryFlag> fromBits(EnumSet<TraceMemoryFlag> flags, int mask) {
|
||||
for (TraceMemoryFlag f : TraceMemoryFlag.values()) {
|
||||
if ((mask & f.getBits()) != 0) {
|
||||
flags.add(f);
|
||||
}
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
public static Collection<TraceMemoryFlag> fromBits(int mask) {
|
||||
return fromBits(EnumSet.noneOf(TraceMemoryFlag.class), mask);
|
||||
}
|
||||
|
||||
public static byte toBits(Collection<TraceMemoryFlag> flags) {
|
||||
byte bits = 0;
|
||||
for (TraceMemoryFlag f : flags) {
|
||||
bits |= f.getBits();
|
||||
}
|
||||
return bits;
|
||||
}
|
||||
|
||||
private final byte bits;
|
||||
|
||||
TraceMemoryFlag(int mask) {
|
||||
|
|
|
@ -21,13 +21,13 @@ import com.google.common.collect.Range;
|
|||
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.TraceObject;
|
||||
import ghidra.trace.model.TraceUniqueObject;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
/**
|
||||
* A region of mapped target memory in a trace
|
||||
*/
|
||||
public interface TraceMemoryRegion extends TraceObject {
|
||||
public interface TraceMemoryRegion extends TraceUniqueObject {
|
||||
|
||||
/**
|
||||
* Get the trace containing this region
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
/* ###
|
||||
* 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.trace.model.memory;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import ghidra.dbg.target.TargetMemoryRegion;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.trace.model.target.TraceObjectInterface;
|
||||
import ghidra.trace.model.target.annot.TraceObjectInfo;
|
||||
|
||||
@TraceObjectInfo(
|
||||
targetIf = TargetMemoryRegion.class,
|
||||
shortName = "region",
|
||||
fixedKeys = {
|
||||
TargetObject.DISPLAY_ATTRIBUTE_NAME,
|
||||
TargetMemoryRegion.RANGE_ATTRIBUTE_NAME
|
||||
})
|
||||
public interface TraceObjectMemoryRegion extends TraceMemoryRegion, TraceObjectInterface {
|
||||
String KEY_VOLATILE = "_volatile";
|
||||
|
||||
void setFlags(Range<Long> lifespan, Collection<TraceMemoryFlag> flags);
|
||||
|
||||
void addFlags(Range<Long> lifespan, Collection<TraceMemoryFlag> flags);
|
||||
|
||||
void clearFlags(Range<Long> lifespan, Collection<TraceMemoryFlag> flags);
|
||||
|
||||
Set<TraceMemoryFlag> getFlags(long snap);
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/* ###
|
||||
* 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.trace.model.memory;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import ghidra.dbg.target.TargetRegister;
|
||||
import ghidra.trace.model.target.TraceObjectInterface;
|
||||
import ghidra.trace.model.target.annot.TraceObjectInfo;
|
||||
import ghidra.trace.model.thread.TraceObjectThread;
|
||||
|
||||
@TraceObjectInfo(
|
||||
// NB. Originally meant to describe the register, it now also describes its value
|
||||
targetIf = TargetRegister.class,
|
||||
shortName = "register",
|
||||
fixedKeys = {
|
||||
TargetRegister.LENGTH_ATTRIBUTE_NAME
|
||||
})
|
||||
public interface TraceObjectRegister extends TraceObjectInterface {
|
||||
String KEY_STATE = "_state";
|
||||
|
||||
TraceObjectThread getThread();
|
||||
|
||||
String getName();
|
||||
|
||||
int getLength();
|
||||
|
||||
void setValue(Range<Long> lifespan, byte[] value);
|
||||
|
||||
byte[] getValue(long snap);
|
||||
|
||||
void setState(Range<Long> lifespan, TraceMemoryState state);
|
||||
|
||||
TraceMemoryState getState(long snap);
|
||||
|
||||
// TODO: getAddress()?
|
||||
// Would provide info for memory-mapped registers.
|
||||
// Could also communicate structure / aliasing.
|
||||
}
|
|
@ -21,7 +21,7 @@ import com.google.common.collect.Range;
|
|||
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.TraceObject;
|
||||
import ghidra.trace.model.TraceUniqueObject;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
/**
|
||||
|
@ -30,7 +30,7 @@ import ghidra.util.exception.DuplicateNameException;
|
|||
* <p>
|
||||
* This also serves as a namespace for storing the module's sections.
|
||||
*/
|
||||
public interface TraceModule extends TraceObject {
|
||||
public interface TraceModule extends TraceUniqueObject {
|
||||
|
||||
/**
|
||||
* Get the trace containing this module
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
/* ###
|
||||
* 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.trace.model.modules;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import ghidra.dbg.target.TargetModule;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.trace.database.module.TraceObjectSection;
|
||||
import ghidra.trace.model.target.TraceObjectInterface;
|
||||
import ghidra.trace.model.target.annot.TraceObjectInfo;
|
||||
|
||||
@TraceObjectInfo(
|
||||
targetIf = TargetModule.class,
|
||||
shortName = "module",
|
||||
fixedKeys = {
|
||||
TargetObject.DISPLAY_ATTRIBUTE_NAME,
|
||||
TargetModule.RANGE_ATTRIBUTE_NAME
|
||||
})
|
||||
public interface TraceObjectModule extends TraceModule, TraceObjectInterface {
|
||||
@Override
|
||||
Collection<? extends TraceObjectSection> getSections();
|
||||
|
||||
@Override
|
||||
TraceObjectSection getSectionByName(String sectionName);
|
||||
}
|
|
@ -18,13 +18,13 @@ package ghidra.trace.model.modules;
|
|||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.TraceObject;
|
||||
import ghidra.trace.model.TraceUniqueObject;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
/**
|
||||
* A section of a module in a trace
|
||||
*/
|
||||
public interface TraceSection extends TraceObject {
|
||||
public interface TraceSection extends TraceUniqueObject {
|
||||
|
||||
/**
|
||||
* Get the trace containing this section
|
||||
|
@ -85,14 +85,16 @@ public interface TraceSection extends TraceObject {
|
|||
* @see #getRange()
|
||||
*/
|
||||
default Address getStart() {
|
||||
return getRange().getMinAddress();
|
||||
AddressRange range = getRange();
|
||||
return range == null ? null : range.getMinAddress();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #getRange()
|
||||
*/
|
||||
default Address getEnd() {
|
||||
return getRange().getMaxAddress();
|
||||
AddressRange range = getRange();
|
||||
return range == null ? null : range.getMaxAddress();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -23,12 +23,12 @@ import ghidra.program.model.address.Address;
|
|||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.TraceObject;
|
||||
import ghidra.trace.model.TraceUniqueObject;
|
||||
|
||||
/**
|
||||
* A mapped range from this trace to a Ghidra {@link Program}
|
||||
*/
|
||||
public interface TraceStaticMapping extends TraceObject {
|
||||
public interface TraceStaticMapping extends TraceUniqueObject {
|
||||
|
||||
/**
|
||||
* Get the "from" trace, i.e., the trace containing this mapping
|
||||
|
|
|
@ -50,7 +50,7 @@ public interface TraceProgramView extends Program {
|
|||
TraceTimeViewport getViewport();
|
||||
|
||||
/**
|
||||
* Get the latest snap
|
||||
* Get the trace's latest snap
|
||||
*
|
||||
* @return the maximum snap
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/* ###
|
||||
* 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.trace.model.stack;
|
||||
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.dbg.target.TargetStack;
|
||||
import ghidra.trace.model.target.TraceObjectInterface;
|
||||
import ghidra.trace.model.target.annot.TraceObjectInfo;
|
||||
|
||||
@TraceObjectInfo(
|
||||
targetIf = TargetStack.class,
|
||||
shortName = "stack",
|
||||
fixedKeys = {
|
||||
TargetObject.DISPLAY_ATTRIBUTE_NAME,
|
||||
})
|
||||
public interface TraceObjectStack extends TraceStack, TraceObjectInterface {
|
||||
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/* ###
|
||||
* 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.trace.model.stack;
|
||||
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.dbg.target.TargetStackFrame;
|
||||
import ghidra.trace.model.target.TraceObjectInterface;
|
||||
import ghidra.trace.model.target.annot.TraceObjectInfo;
|
||||
|
||||
@TraceObjectInfo(
|
||||
targetIf = TargetStackFrame.class,
|
||||
shortName = "frame",
|
||||
fixedKeys = {
|
||||
TargetObject.DISPLAY_ATTRIBUTE_NAME,
|
||||
})
|
||||
public interface TraceObjectStackFrame extends TraceStackFrame, TraceObjectInterface {
|
||||
|
||||
}
|
|
@ -17,6 +17,7 @@ package ghidra.trace.model.stack;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.trace.model.TraceUniqueObject;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
|
||||
/**
|
||||
|
@ -29,7 +30,7 @@ import ghidra.trace.model.thread.TraceThread;
|
|||
* for that analysis. If this information wasn't recorded during a session, this can store the
|
||||
* result of that analysis.
|
||||
*/
|
||||
public interface TraceStack {
|
||||
public interface TraceStack extends TraceUniqueObject {
|
||||
|
||||
/**
|
||||
* Get the thread whose stack this is
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
/* ###
|
||||
* 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.trace.model.target;
|
||||
|
||||
public class DuplicateKeyException extends RuntimeException {
|
||||
public DuplicateKeyException(String key) {
|
||||
super(key);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,444 @@
|
|||
/* ###
|
||||
* 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.trace.model.target;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.dbg.target.schema.TargetObjectSchema;
|
||||
import ghidra.dbg.util.PathPredicates;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.TraceUniqueObject;
|
||||
|
||||
/**
|
||||
* The trace record of an observed {@link TargetObject}
|
||||
*
|
||||
* <p>
|
||||
* See {@link TargetObject} for information about how objects and the model schema are related in a
|
||||
* debugger model. This trace object records a target object and a subset of its children into the
|
||||
* database with additional timing information. For objects implementing specific
|
||||
* {@link TargetObject} interfaces, a corresponding {@link TraceObjectInterface} can be retrieved.
|
||||
* In many cases, such interfaces are just wrappers.
|
||||
*/
|
||||
public interface TraceObject extends TraceUniqueObject {
|
||||
/**
|
||||
* Get the trace containing this object
|
||||
*
|
||||
* @return the trace
|
||||
*/
|
||||
Trace getTrace();
|
||||
|
||||
/**
|
||||
* Get the root of the tree containing this object
|
||||
*
|
||||
* @return the root
|
||||
*/
|
||||
TraceObject getRoot();
|
||||
|
||||
/**
|
||||
* Get the canonical path of this object
|
||||
*
|
||||
* @return the path
|
||||
*/
|
||||
TraceObjectKeyPath getCanonicalPath();
|
||||
|
||||
/**
|
||||
* Inserts this object at its canonical path for its lifespan
|
||||
*
|
||||
* <p>
|
||||
* Any ancestor which does not exist is created with the same lifespan as this object. Values
|
||||
* are set with the same lifespan. Only the canonical path is considered when looking for
|
||||
* existing ancestry. Any whose lifespan intersects that of this object is considered
|
||||
* "existing." If an existing ancestor is detached, this object will still become its successor,
|
||||
* and the resulting subtree will remain detached.
|
||||
*
|
||||
* @param resolution the rule for handling duplicate keys when setting values.
|
||||
*/
|
||||
void insert(ConflictResolution resolution);
|
||||
|
||||
/**
|
||||
* Check if this object is the root
|
||||
*
|
||||
* @return true if root
|
||||
*/
|
||||
boolean isRoot();
|
||||
|
||||
/**
|
||||
* Get all paths actually leading to this object, from the root, within the given span
|
||||
*
|
||||
* @param span the span which every value entry on each path must intersect
|
||||
* @return the paths
|
||||
*/
|
||||
Stream<TraceObjectValPath> getAllPaths(Range<Long> span);
|
||||
|
||||
/**
|
||||
* Specifies a strategy for resolving duplicate keys
|
||||
*
|
||||
* <p>
|
||||
* Values are not permitted to have intersecting lifespans if they have the same parent and key,
|
||||
* since this would imply the value is not unique for a given parent, key, and snap. Thus, when
|
||||
* values and lifespans are being set that would result in conflicting entries, the conflict
|
||||
* must be resolved, either by clearing the span or by denying the change.
|
||||
*/
|
||||
enum ConflictResolution {
|
||||
/**
|
||||
* Truncate, split, or delete conflicting entries to make way for the specified lifespan
|
||||
*/
|
||||
TRUNCATE,
|
||||
/**
|
||||
* Throw an exception if the specified lifespan would result in conflicting entries
|
||||
*/
|
||||
DENY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the lifespan of this object
|
||||
*
|
||||
* <p>
|
||||
* NOTE: Objects with intersecting lifespans are not checked for duplicate canonical paths.
|
||||
* However, their parent value entries are checked for conflicts. Thus, at any snap, it is
|
||||
* impossible for any two objects with equal canonical paths to both exist at their canonical
|
||||
* locations.
|
||||
*
|
||||
* @param lifespan the new lifespan
|
||||
*/
|
||||
void setLifespan(Range<Long> lifespan);
|
||||
|
||||
/**
|
||||
* Get the lifespan of this object
|
||||
*
|
||||
* @return the lifespan
|
||||
*/
|
||||
Range<Long> getLifespan();
|
||||
|
||||
/**
|
||||
* Set the minimum snap of this object
|
||||
*
|
||||
* @see #setLifespan(Range)
|
||||
* @param minSnap the minimum snap, or {@link Long#MIN_VALUE} for "since the beginning of time"
|
||||
*/
|
||||
void setMinSnap(long minSnap);
|
||||
|
||||
/**
|
||||
* Get the minimum snap of this object
|
||||
*
|
||||
* @return the minimum snap, or {@link Long#MIN_VALUE} for "since the beginning of time"
|
||||
*/
|
||||
long getMinSnap();
|
||||
|
||||
/**
|
||||
* Set the maximum snap of this object
|
||||
*
|
||||
* @see #setLifespan(Range)
|
||||
* @param maxSnap the maximum snap, or {@link Long#MAX_VALUE} for "to the end of time"
|
||||
*/
|
||||
void setMaxSnap(long maxSnap);
|
||||
|
||||
/**
|
||||
* Get the maximum snap of this object
|
||||
*
|
||||
* @return the maximum snap, or {@link Long#MAX_VALUE} for "to the end of time"
|
||||
*/
|
||||
long getMaxSnap();
|
||||
|
||||
/**
|
||||
* Get all the interface classes provided by this object, according to the schema
|
||||
*
|
||||
* @return the collection of interface classes
|
||||
*/
|
||||
Collection<Class<? extends TraceObjectInterface>> getInterfaces();
|
||||
|
||||
/**
|
||||
* Request the specified interface provided by this object
|
||||
*
|
||||
* @param <I> the type of the interface
|
||||
* @param ifClass the class of the interface
|
||||
* @return the interface, or null if not provided
|
||||
*/
|
||||
<I extends TraceObjectInterface> I queryInterface(Class<I> ifClass);
|
||||
|
||||
/**
|
||||
* Get all values whose child is this object
|
||||
*
|
||||
* @return the parent values
|
||||
*/
|
||||
Collection<? extends TraceObjectValue> getParents();
|
||||
|
||||
/**
|
||||
* Get all values (elements and attributes) of this object
|
||||
*
|
||||
* @return the values
|
||||
*/
|
||||
Collection<? extends TraceObjectValue> getValues();
|
||||
|
||||
/**
|
||||
* Get values with the given key intersecting the given span ordered by time
|
||||
*
|
||||
* @param span the span
|
||||
* @param key the key
|
||||
* @param forward true to order from least- to most-recent, false for most- to least-recent
|
||||
* @return the stream of values
|
||||
*/
|
||||
Stream<? extends TraceObjectValue> getOrderedValues(Range<Long> span, String key,
|
||||
boolean forward);
|
||||
|
||||
/**
|
||||
* Get all elements of this object
|
||||
*
|
||||
* @return the element values
|
||||
*/
|
||||
Collection<? extends TraceObjectValue> getElements();
|
||||
|
||||
/**
|
||||
* Get all attributes of this object
|
||||
*
|
||||
* @return the attribute values
|
||||
*/
|
||||
Collection<? extends TraceObjectValue> getAttributes();
|
||||
|
||||
/**
|
||||
* Get the value for the given snap and key
|
||||
*
|
||||
* @param snap the snap
|
||||
* @param key the key
|
||||
* @return the value entry
|
||||
*/
|
||||
TraceObjectValue getValue(long snap, String key);
|
||||
|
||||
/**
|
||||
* Get the value for the given snap and element index
|
||||
*
|
||||
* <p>
|
||||
* This is equivalent to {@link #getValue(long, String)}, but converts index to a key, i.e.,
|
||||
* adds brackets.
|
||||
*
|
||||
* @param snap the snap
|
||||
* @param index the index
|
||||
* @return the value entry
|
||||
*/
|
||||
TraceObjectValue getElement(long snap, String index);
|
||||
|
||||
/**
|
||||
* Get the value for the given snap and element index
|
||||
*
|
||||
* <p>
|
||||
* This is equivalent to {@link #getElement(long, String)}, but converts index to a string in
|
||||
* decimal.
|
||||
*
|
||||
* @param snap the snap
|
||||
* @param index the index
|
||||
* @return the value entry
|
||||
*/
|
||||
TraceObjectValue getElement(long snap, long index);
|
||||
|
||||
/**
|
||||
* Get the value for the given snap and attribute name
|
||||
*
|
||||
* <p>
|
||||
* This is equivalent to {@link #getValue(long, String)}, except it validates that name is not
|
||||
* an index.
|
||||
*
|
||||
* @param snap the snap
|
||||
* @param name the name
|
||||
* @return the value entry
|
||||
*/
|
||||
TraceObjectValue getAttribute(long snap, String name);
|
||||
|
||||
/**
|
||||
* Stream all ancestor values of this object matching the given predicates, intersecting the
|
||||
* given span
|
||||
*
|
||||
* @param span a span which values along the path must intersect
|
||||
* @param rootPredicates the predicates for matching path keys, relative to the root
|
||||
* @return the stream of matching paths to values
|
||||
*/
|
||||
Stream<? extends TraceObjectValPath> getAncestors(Range<Long> span,
|
||||
PathPredicates rootPredicates);
|
||||
|
||||
/**
|
||||
* Stream all successor values of this object matching the given predicates, intersecting the
|
||||
* given span
|
||||
*
|
||||
* @param span a span which values along the path must intersect
|
||||
* @param relativePredicates the predicates for matching path keys, relative to this object
|
||||
* @return the stream of matching paths to values
|
||||
*/
|
||||
Stream<? extends TraceObjectValPath> getSuccessors(Range<Long> span,
|
||||
PathPredicates relativePredicates);
|
||||
|
||||
/**
|
||||
* Stream all successor values of this object at the given relative path, intersecting the given
|
||||
* span, ordered by time.
|
||||
*
|
||||
* @param span the span which values along the path must intersect
|
||||
* @param relativePath the path relative to this object
|
||||
* @param forward true to order from least- to most-recent, false for most- to least-recent
|
||||
* @return the stream of value paths
|
||||
*/
|
||||
Stream<? extends TraceObjectValPath> getOrderedSuccessors(Range<Long> span,
|
||||
TraceObjectKeyPath relativePath, boolean forward);
|
||||
|
||||
/**
|
||||
* Set a value for the given lifespan
|
||||
*
|
||||
* @param lifespan the lifespan of the value
|
||||
* @param key the key to set
|
||||
* @param value the new value
|
||||
* @param resolution determines how to resolve conflicting keys with intersecting lifespans
|
||||
* @return the created value entry
|
||||
*/
|
||||
TraceObjectValue setValue(Range<Long> lifespan, String key, Object value,
|
||||
ConflictResolution resolution);
|
||||
|
||||
/**
|
||||
* Set a value for the given lifespan, truncating existing entries
|
||||
*
|
||||
* @param lifespan the lifespan of the value
|
||||
* @param key the key to set
|
||||
* @param value the new value
|
||||
* @return the created value entry
|
||||
*/
|
||||
TraceObjectValue setValue(Range<Long> lifespan, String key, Object value);
|
||||
|
||||
/**
|
||||
* Set an attribute for the given lifespan
|
||||
*
|
||||
* <p>
|
||||
* This is equivalent to {@link #setValue(Range, String, Object)}, except it verifies the key is
|
||||
* an attribute name.
|
||||
*
|
||||
* @param lifespan the lifespan of the attribute
|
||||
* @param name the name to set
|
||||
* @param value the new value
|
||||
* @return the created value entry
|
||||
*/
|
||||
TraceObjectValue setAttribute(Range<Long> lifespan, String name, Object value);
|
||||
|
||||
/**
|
||||
* Set an element for the given lifespan
|
||||
*
|
||||
* <p>
|
||||
* This is equivalent to {@link #setValue(Range, String, Object)}, except it converts the index
|
||||
* to a key, i.e., add brackets.
|
||||
*
|
||||
* @param lifespan the lifespan of the element
|
||||
* @param index the index to set
|
||||
* @param value the new value
|
||||
* @return the created value entry
|
||||
*/
|
||||
TraceObjectValue setElement(Range<Long> lifespan, String index, Object value);
|
||||
|
||||
/**
|
||||
* Set an element for the given lifespan
|
||||
*
|
||||
* @param lifespan the lifespan of the element
|
||||
* @param index the index to set
|
||||
* @param value the new value
|
||||
* @return the created value entry
|
||||
*/
|
||||
TraceObjectValue setElement(Range<Long> lifespan, long index, Object value);
|
||||
|
||||
/**
|
||||
* Get the (target) schema for this object
|
||||
*
|
||||
* @return the schema
|
||||
*/
|
||||
TargetObjectSchema getTargetSchema();
|
||||
|
||||
/**
|
||||
* Search for ancestors providing the given interface and retrieve those interfaces
|
||||
*
|
||||
* @param <I> the interface type
|
||||
* @param span the span which the found objects must intersect
|
||||
* @param ifClass the interface class
|
||||
* @return the stream of interfaces
|
||||
*/
|
||||
<I extends TraceObjectInterface> Stream<I> queryAncestorsInterface(Range<Long> span,
|
||||
Class<I> ifClass);
|
||||
|
||||
/**
|
||||
* Search for ancestors on the canonical path providing the given interface
|
||||
*
|
||||
* <p>
|
||||
* The object may not yet be inserted at its canonical path
|
||||
*
|
||||
* @param <I> the interface type
|
||||
* @param span the span which the found objects must intersect
|
||||
* @param ifClass the interface class
|
||||
* @return the stream of interfaces
|
||||
*/
|
||||
<I extends TraceObjectInterface> Stream<I> queryCanonicalAncestorsInterface(
|
||||
Range<Long> span, Class<I> ifClass);
|
||||
|
||||
/**
|
||||
* Search for successors providing the given interface and retrieve those interfaces
|
||||
*
|
||||
* @param <I> the interface type
|
||||
* @param span the span which the found objects must intersect
|
||||
* @param ifClass the interface class
|
||||
* @return the stream of interfaces
|
||||
*/
|
||||
<I extends TraceObjectInterface> Stream<I> querySuccessorsInterface(Range<Long> span,
|
||||
Class<I> ifClass);
|
||||
|
||||
/**
|
||||
* Delete this object along with parent and child value entries referring to it
|
||||
*
|
||||
* <p>
|
||||
* Note, this does not delete the children or any successors. Use {@link #deleteTree()} to
|
||||
* delete an entire subtree, regardless of lifespan. It is not recommended to invoke this on the
|
||||
* root object, since it cannot be replaced without first clearing the manager.
|
||||
*/
|
||||
void delete();
|
||||
|
||||
/**
|
||||
* Delete this object and its successors along with value entries referring to any
|
||||
*
|
||||
* <p>
|
||||
* It is not recommended to invoke this on the root object. Instead, use
|
||||
* {@link TraceObjectManager#clear()}. The root object cannot be replaced without first clearing
|
||||
* the manager.
|
||||
*/
|
||||
void deleteTree();
|
||||
|
||||
/**
|
||||
* Check if this object has been deleted
|
||||
*
|
||||
* @return true if the object has been deleted
|
||||
*/
|
||||
@Override
|
||||
boolean isDeleted();
|
||||
|
||||
/**
|
||||
* Modify the lifespan or delete this object, such that it no longer intersects the given span.
|
||||
*
|
||||
* <p>
|
||||
* If the given span and the current lifespan are already disjoint, this does nothing. If the
|
||||
* given span splits the current lifespan in two, an exception is thrown. This is because the
|
||||
* two resulting objects ought to be identical, but they cannot be. Instead the one object
|
||||
* should remain alive, and the edge(s) pointing to it should be truncated. In other words, a
|
||||
* single object cannot vanish and then later re-appear, but it can be unlinked and then later
|
||||
* become relinked.
|
||||
*
|
||||
* @param span the span to clear
|
||||
* @return this if the one object remains, null if the object is deleted.
|
||||
* @throws IllegalArgumentException if the given span splits the current lifespan in two
|
||||
*/
|
||||
TraceObject truncateOrDelete(Range<Long> span);
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
/* ###
|
||||
* 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.trace.model.target;
|
||||
|
||||
public interface TraceObjectInterface {
|
||||
TraceObject getObject();
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
/* ###
|
||||
* 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.trace.model.target;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.dbg.util.PathUtils;
|
||||
import ghidra.dbg.util.PathUtils.PathComparator;
|
||||
|
||||
public final class TraceObjectKeyPath implements Comparable<TraceObjectKeyPath> {
|
||||
|
||||
public static TraceObjectKeyPath of(List<String> keyList) {
|
||||
return new TraceObjectKeyPath(List.copyOf(keyList));
|
||||
}
|
||||
|
||||
public static TraceObjectKeyPath of(String... keys) {
|
||||
return new TraceObjectKeyPath(List.of(keys));
|
||||
}
|
||||
|
||||
public static TraceObjectKeyPath parse(String path) {
|
||||
return new TraceObjectKeyPath(PathUtils.parse(path));
|
||||
}
|
||||
|
||||
private final List<String> keyList;
|
||||
private final int hash;
|
||||
|
||||
private TraceObjectKeyPath(List<String> keyList) {
|
||||
this.keyList = keyList;
|
||||
this.hash = Objects.hash(keyList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(TraceObjectKeyPath that) {
|
||||
if (this == that) {
|
||||
return 0;
|
||||
}
|
||||
return PathComparator.KEYED.compare(this.keyList, that.keyList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof TraceObjectKeyPath)) {
|
||||
return false;
|
||||
}
|
||||
TraceObjectKeyPath that = (TraceObjectKeyPath) obj;
|
||||
return this.keyList.equals(that.keyList);
|
||||
}
|
||||
|
||||
public List<String> getKeyList() {
|
||||
return keyList;
|
||||
}
|
||||
|
||||
public boolean isRoot() {
|
||||
return keyList.isEmpty();
|
||||
}
|
||||
|
||||
public TraceObjectKeyPath key(String name) {
|
||||
return new TraceObjectKeyPath(PathUtils.extend(keyList, name));
|
||||
}
|
||||
|
||||
public String key() {
|
||||
return PathUtils.getKey(keyList);
|
||||
}
|
||||
|
||||
public TraceObjectKeyPath index(long index) {
|
||||
return index(PathUtils.makeIndex(index));
|
||||
}
|
||||
|
||||
public TraceObjectKeyPath index(String index) {
|
||||
return new TraceObjectKeyPath(PathUtils.index(keyList, index));
|
||||
}
|
||||
|
||||
public String index() {
|
||||
return PathUtils.getIndex(keyList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return PathUtils.toString(keyList);
|
||||
}
|
||||
|
||||
public TraceObjectKeyPath parent() {
|
||||
List<String> pkl = PathUtils.parent(keyList);
|
||||
return pkl == null ? null : new TraceObjectKeyPath(pkl);
|
||||
}
|
||||
|
||||
public TraceObjectKeyPath extend(List<String> subKeyList) {
|
||||
return new TraceObjectKeyPath(PathUtils.extend(keyList, subKeyList));
|
||||
}
|
||||
|
||||
public TraceObjectKeyPath extend(String... subKeyList) {
|
||||
return extend(Arrays.asList(subKeyList));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,163 @@
|
|||
/* ###
|
||||
* 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.trace.model.target;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import ghidra.dbg.DebuggerObjectModel;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.dbg.target.schema.TargetObjectSchema;
|
||||
import ghidra.dbg.util.PathPredicates;
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.trace.model.Trace;
|
||||
|
||||
/**
|
||||
* A store of objects observed over time in a trace
|
||||
*/
|
||||
public interface TraceObjectManager {
|
||||
|
||||
/**
|
||||
* Get the trace to which the object manager belongs
|
||||
*
|
||||
* @return the trace
|
||||
*/
|
||||
Trace getTrace();
|
||||
|
||||
/**
|
||||
* Creates the root object of the model, fixing its schema
|
||||
*
|
||||
* <p>
|
||||
* Note the schema cannot be changed once the root object is created. The only means to "change"
|
||||
* the schema is to delete the root object (and thus the entire tree) then re-create the root
|
||||
* object with the new schema.
|
||||
*
|
||||
* @param schema the schema
|
||||
* @return the new object
|
||||
*/
|
||||
TraceObjectValue createRootObject(TargetObjectSchema schema);
|
||||
|
||||
/**
|
||||
* Create an object with the given canonical path having the given lifespan
|
||||
*
|
||||
* @param path the object's canonical path
|
||||
* @param lifespan the initial lifespan
|
||||
* @return the new object
|
||||
*/
|
||||
TraceObject createObject(TraceObjectKeyPath path, Range<Long> lifespan);
|
||||
|
||||
/**
|
||||
* Get the schema of the root object
|
||||
*
|
||||
* <p>
|
||||
* NOTE: The interface classes specified by the schema are those for {@link TargetObject}. The
|
||||
* interfaces in the database are different, and not all may have an analog.
|
||||
*
|
||||
* @return the schema
|
||||
*/
|
||||
TargetObjectSchema getRootSchema();
|
||||
|
||||
/**
|
||||
* Get the root object, if it has been created
|
||||
*
|
||||
* @return the root object, or null
|
||||
*/
|
||||
TraceObject getRootObject();
|
||||
|
||||
/**
|
||||
* Get the object with the given database key, if it exists
|
||||
*
|
||||
* @param key the desired object's key
|
||||
* @return the object, or null
|
||||
*/
|
||||
TraceObject getObjectById(long key);
|
||||
|
||||
/**
|
||||
* Get objects in the database having the given canonical path
|
||||
*
|
||||
* @param path the canonical path of the desired objects
|
||||
* @return the collection of objects
|
||||
*/
|
||||
Collection<? extends TraceObject> getObjectsByCanonicalPath(TraceObjectKeyPath path);
|
||||
|
||||
/**
|
||||
* Get objects in the database having the given path intersecting the given span
|
||||
*
|
||||
* @param path the path of the desired objects
|
||||
* @param span the span that desired objects' lifespans must intersect
|
||||
* @return the iterable of objects
|
||||
*/
|
||||
Stream<? extends TraceObject> getObjectsByPath(Range<Long> span,
|
||||
TraceObjectKeyPath path);
|
||||
|
||||
/**
|
||||
* Get value entries in the database matching the given predicates intersecting the given span
|
||||
*
|
||||
* <p>
|
||||
* While the manager does not maintain integrity wrt. child lifespans and that of their parents,
|
||||
* nor even the connectivity of objects to their canonical parents, this search depends on that
|
||||
* consistency. An object may not be discovered unless it is properly connected to the root
|
||||
* object. Furthermore, it will not be discovered unless it and its ancestors' lifespans all
|
||||
* intersect the given span.
|
||||
*
|
||||
* @param span the span that desired objects' lifespans must intersect
|
||||
* @param predicates predicates to match the desired objects
|
||||
* @return an iterator over the matching objects
|
||||
*/
|
||||
Stream<? extends TraceObjectValPath> getValuePaths(Range<Long> span,
|
||||
PathPredicates predicates);
|
||||
|
||||
/**
|
||||
* Get all the objects in the database
|
||||
*
|
||||
* @return the collection of all objects
|
||||
*/
|
||||
Collection<? extends TraceObject> getAllObjects();
|
||||
|
||||
/**
|
||||
* Get all address-ranged values intersecting the given span and address range
|
||||
*
|
||||
* @param span the span that desired values lifespans must intersect
|
||||
* @param range the range that desired address-ranged values must intersect
|
||||
* @return the collection of values
|
||||
*/
|
||||
Collection<? extends TraceObjectValue> getValuesIntersecting(Range<Long> span,
|
||||
AddressRange range);
|
||||
|
||||
/**
|
||||
* Get all interfaces of the given type in the database
|
||||
*
|
||||
* @param <I> the type of the desired interface
|
||||
* @param span the span that desired objects must intersect
|
||||
* @param ifClass the class of the desired interface
|
||||
* @return the collection of all instances of the given interface
|
||||
*/
|
||||
<I extends TraceObjectInterface> Stream<I> queryAllInterface(Range<Long> span,
|
||||
Class<I> ifClass);
|
||||
|
||||
/**
|
||||
* Delete the <em>entire object model</em>, including the schema
|
||||
*
|
||||
* <p>
|
||||
* This is the only mechanism to modify the schema. This should almost never be necessary,
|
||||
* because a {@link DebuggerObjectModel} should provide its immutable schema immediately.
|
||||
* Nevertheless, the database permits schema modification, but requires that the entire model be
|
||||
* replaced.
|
||||
*/
|
||||
void clear();
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue