Merge remote-tracking branch 'origin/GP-1386_Dan_DBTraceObjectModel-REBASED-1--SQUASHED'

This commit is contained in:
ghidra1 2022-01-06 15:26:51 -05:00
commit 8e59d0e673
179 changed files with 9971 additions and 797 deletions

View file

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

View file

@ -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.

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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()),

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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(),

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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,

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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,

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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) {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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
*/

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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