Merge remote-tracking branch 'origin/GP-2191_Dan_makePublicTraceAddressPropertyManager--SQUASHED'

This commit is contained in:
Ryan Kurtz 2022-06-21 13:43:50 -04:00
commit 4ef111155a
16 changed files with 713 additions and 119 deletions

View file

@ -55,6 +55,7 @@ import ghidra.trace.database.time.DBTraceTimeManager;
import ghidra.trace.model.Trace; import ghidra.trace.model.Trace;
import ghidra.trace.model.memory.TraceMemoryRegion; import ghidra.trace.model.memory.TraceMemoryRegion;
import ghidra.trace.model.program.TraceProgramView; import ghidra.trace.model.program.TraceProgramView;
import ghidra.trace.model.property.TraceAddressPropertyManager;
import ghidra.trace.util.TraceChangeManager; import ghidra.trace.util.TraceChangeManager;
import ghidra.trace.util.TraceChangeRecord; import ghidra.trace.util.TraceChangeRecord;
import ghidra.util.*; import ghidra.util.*;
@ -456,8 +457,13 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
return baseAddressFactory; return baseAddressFactory;
} }
@Override
public TraceAddressPropertyManager getAddressPropertyManager() {
return addressPropertyManager.getApiPropertyManager();
}
@Internal @Internal
public DBTraceAddressPropertyManager getAddressPropertyManager() { public DBTraceAddressPropertyManager getInternalAddressPropertyManager() {
return addressPropertyManager; return addressPropertyManager;
} }

View file

@ -33,9 +33,9 @@ import ghidra.program.model.symbol.*;
import ghidra.trace.database.DBTrace; import ghidra.trace.database.DBTrace;
import ghidra.trace.database.symbol.DBTraceReference; import ghidra.trace.database.symbol.DBTraceReference;
import ghidra.trace.model.listing.TraceCodeUnit; import ghidra.trace.model.listing.TraceCodeUnit;
import ghidra.trace.model.map.TracePropertyMap;
import ghidra.trace.model.memory.TraceMemoryRegion; import ghidra.trace.model.memory.TraceMemoryRegion;
import ghidra.trace.model.program.TraceProgramView; import ghidra.trace.model.program.TraceProgramView;
import ghidra.trace.model.property.*;
import ghidra.trace.model.symbol.TraceReference; import ghidra.trace.model.symbol.TraceReference;
import ghidra.trace.model.symbol.TraceSymbol; import ghidra.trace.model.symbol.TraceSymbol;
import ghidra.trace.model.thread.TraceThread; import ghidra.trace.model.thread.TraceThread;
@ -84,8 +84,9 @@ public interface DBTraceCodeUnitAdapter extends TraceCodeUnit, MemBufferAdapter
@Override @Override
default <T> void setProperty(String name, Class<T> valueClass, T value) { default <T> void setProperty(String name, Class<T> valueClass, T value) {
try (LockHold hold = LockHold.lock(getTrace().getReadWriteLock().writeLock())) { try (LockHold hold = LockHold.lock(getTrace().getReadWriteLock().writeLock())) {
TracePropertyMap<? super T> setter = TracePropertySetter<T> setter =
getTrace().getAddressPropertyManager().getOrCreatePropertySetter(name, valueClass); getTrace().getInternalAddressPropertyManager()
.getOrCreatePropertySetter(name, valueClass);
setter.set(getLifespan(), getAddress(), value); setter.set(getLifespan(), getAddress(), value);
} }
} }
@ -121,8 +122,8 @@ public interface DBTraceCodeUnitAdapter extends TraceCodeUnit, MemBufferAdapter
@Override @Override
default <T> T getProperty(String name, Class<T> valueClass) { default <T> T getProperty(String name, Class<T> valueClass) {
try (LockHold hold = LockHold.lock(getTrace().getReadWriteLock().readLock())) { try (LockHold hold = LockHold.lock(getTrace().getReadWriteLock().readLock())) {
TracePropertyMap<? extends T> getter = TracePropertyGetter<T> getter =
getTrace().getAddressPropertyManager().getPropertyGetter(name, valueClass); getTrace().getInternalAddressPropertyManager().getPropertyGetter(name, valueClass);
return getter.get(getStartSnap(), getAddress()); return getter.get(getStartSnap(), getAddress());
} }
} }
@ -149,13 +150,13 @@ public interface DBTraceCodeUnitAdapter extends TraceCodeUnit, MemBufferAdapter
@Override @Override
default boolean hasProperty(String name) { default boolean hasProperty(String name) {
try (LockHold hold = LockHold.lock(getTrace().getReadWriteLock().readLock())) { try (LockHold hold = LockHold.lock(getTrace().getReadWriteLock().readLock())) {
TracePropertyMap<?> map = getTrace().getAddressPropertyManager().getPropertyMap(name); TracePropertyMap<?> map =
getTrace().getInternalAddressPropertyManager().getPropertyMap(name);
if (map == null) { if (map == null) {
return false; return false;
} }
// NOTE: Properties all defined at start snap // NOTE: Properties all defined at start snap
return map.getAddressSetView(Range.closed(getStartSnap(), getStartSnap())) return map.getAddressSetView(Range.singleton(getStartSnap())).contains(getAddress());
.contains(getAddress());
} }
} }
@ -163,29 +164,28 @@ public interface DBTraceCodeUnitAdapter extends TraceCodeUnit, MemBufferAdapter
default boolean getVoidProperty(String name) { default boolean getVoidProperty(String name) {
// NOTE: Nearly identical to hasProperty, except named property must be Void type // NOTE: Nearly identical to hasProperty, except named property must be Void type
try (LockHold hold = LockHold.lock(getTrace().getReadWriteLock().readLock())) { try (LockHold hold = LockHold.lock(getTrace().getReadWriteLock().readLock())) {
TracePropertyMap<? extends Void> getter = TracePropertyGetter<Void> getter =
getTrace().getAddressPropertyManager().getPropertyGetter(name, Void.class); getTrace().getInternalAddressPropertyManager().getPropertyGetter(name, Void.class);
if (getter == null) { if (getter == null) {
return false; return false;
} }
return getter.getAddressSetView(Range.closed(getStartSnap(), getStartSnap())) return getter.getAddressSetView(Range.singleton(getStartSnap())).contains(getAddress());
.contains(
getAddress());
} }
} }
@Override @Override
default Iterator<String> propertyNames() { default Iterator<String> propertyNames() {
Range<Long> closed = Range.closed(getStartSnap(), getStartSnap()); Range<Long> span = Range.singleton(getStartSnap());
return Iterators.transform(Iterators.filter( return Iterators.transform(Iterators.filter(
getTrace().getAddressPropertyManager().getAllProperties().entrySet().iterator(), getTrace().getInternalAddressPropertyManager().getAllProperties().entrySet().iterator(),
e -> e.getValue().getAddressSetView(closed).contains(getAddress())), Entry::getKey); e -> e.getValue().getAddressSetView(span).contains(getAddress())), Entry::getKey);
} }
@Override @Override
default void removeProperty(String name) { default void removeProperty(String name) {
try (LockHold hold = LockHold.lock(getTrace().getReadWriteLock().writeLock())) { try (LockHold hold = LockHold.lock(getTrace().getReadWriteLock().writeLock())) {
TracePropertyMap<?> map = getTrace().getAddressPropertyManager().getPropertyMap(name); TracePropertyMap<?> map =
getTrace().getInternalAddressPropertyManager().getPropertyMap(name);
if (map == null) { if (map == null) {
return; return;
} }
@ -197,14 +197,12 @@ public interface DBTraceCodeUnitAdapter extends TraceCodeUnit, MemBufferAdapter
default void visitProperty(PropertyVisitor visitor, String propertyName) { default void visitProperty(PropertyVisitor visitor, String propertyName) {
try (LockHold hold = LockHold.lock(getTrace().getReadWriteLock().readLock())) { try (LockHold hold = LockHold.lock(getTrace().getReadWriteLock().readLock())) {
TracePropertyMap<?> map = TracePropertyMap<?> map =
getTrace().getAddressPropertyManager().getPropertyMap(propertyName); getTrace().getInternalAddressPropertyManager().getPropertyMap(propertyName);
if (map == null) { if (map == null) {
return; return;
} }
if (map.getValueClass() == Void.class) { if (map.getValueClass() == Void.class) {
if (map.getAddressSetView(Range.closed(getStartSnap(), getStartSnap())) if (map.getAddressSetView(Range.singleton(getStartSnap())).contains(getAddress())) {
.contains(
getAddress())) {
visitor.visit(); visitor.visit();
} }
return; return;
@ -437,5 +435,4 @@ public interface DBTraceCodeUnitAdapter extends TraceCodeUnit, MemBufferAdapter
throw new MemoryAccessException("Couldn't get requested bytes for CodeUnit"); throw new MemoryAccessException("Couldn't get requested bytes for CodeUnit");
} }
} }
} }

View file

@ -33,7 +33,7 @@ import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.Abstract
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.TraceAddressSnapRangeQuery; import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.TraceAddressSnapRangeQuery;
import ghidra.trace.database.thread.DBTraceThreadManager; import ghidra.trace.database.thread.DBTraceThreadManager;
import ghidra.trace.model.TraceAddressSnapRange; import ghidra.trace.model.TraceAddressSnapRange;
import ghidra.trace.model.map.TracePropertyMap; import ghidra.trace.model.property.TracePropertyMap;
import ghidra.util.*; import ghidra.util.*;
import ghidra.util.database.*; import ghidra.util.database.*;
import ghidra.util.database.DBCachedObjectStoreFactory.AbstractDBFieldCodec; import ghidra.util.database.DBCachedObjectStoreFactory.AbstractDBFieldCodec;
@ -71,11 +71,21 @@ public abstract class AbstractDBTracePropertyMap<T, DR extends AbstractDBTraceAd
put(address, lifespan, value); put(address, lifespan, value);
} }
@Override
public void set(Range<Long> lifespan, AddressRange range, T value) {
put(range, lifespan, value);
}
@Override @Override
public T get(long snap, Address address) { public T get(long snap, Address address) {
return reduce(TraceAddressSnapRangeQuery.at(address, snap)).firstValue(); return reduce(TraceAddressSnapRangeQuery.at(address, snap)).firstValue();
} }
@Override
public Entry<TraceAddressSnapRange, T> getEntry(long snap, Address address) {
return reduce(TraceAddressSnapRangeQuery.at(address, snap)).firstEntry();
}
@Override @Override
public void clear(Range<Long> span, AddressRange range) { public void clear(Range<Long> span, AddressRange range) {
try (LockHold hold = LockHold.lock(lock.writeLock())) { try (LockHold hold = LockHold.lock(lock.writeLock())) {
@ -88,7 +98,6 @@ public abstract class AbstractDBTracePropertyMap<T, DR extends AbstractDBTraceAd
@Override @Override
public T put(TraceAddressSnapRange shape, T value) { public T put(TraceAddressSnapRange shape, T value) {
assert shape.getRange().getLength() == 1;
try (LockHold hold = LockHold.lock(lock.writeLock())) { try (LockHold hold = LockHold.lock(lock.writeLock())) {
for (Entry<TraceAddressSnapRange, T> entry : reduce( for (Entry<TraceAddressSnapRange, T> entry : reduce(
TraceAddressSnapRangeQuery.intersecting(shape)).entries()) { TraceAddressSnapRangeQuery.intersecting(shape)).entries()) {

View file

@ -43,10 +43,10 @@ import ghidra.trace.database.symbol.DBTraceFunctionSymbol;
import ghidra.trace.database.thread.DBTraceThread; import ghidra.trace.database.thread.DBTraceThread;
import ghidra.trace.model.*; import ghidra.trace.model.*;
import ghidra.trace.model.listing.*; import ghidra.trace.model.listing.*;
import ghidra.trace.model.map.TracePropertyMap;
import ghidra.trace.model.memory.TraceMemoryRegion; import ghidra.trace.model.memory.TraceMemoryRegion;
import ghidra.trace.model.program.TraceProgramView; import ghidra.trace.model.program.TraceProgramView;
import ghidra.trace.model.program.TraceProgramViewListing; import ghidra.trace.model.program.TraceProgramViewListing;
import ghidra.trace.model.property.TracePropertyMap;
import ghidra.trace.model.symbol.TraceFunctionSymbol; import ghidra.trace.model.symbol.TraceFunctionSymbol;
import ghidra.trace.util.*; import ghidra.trace.util.*;
import ghidra.util.*; import ghidra.util.*;
@ -358,7 +358,7 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
// TODO: Cover this in testing // TODO: Cover this in testing
TracePropertyMap<?> map = TracePropertyMap<?> map =
program.trace.getAddressPropertyManager().getPropertyMap(property); program.trace.getInternalAddressPropertyManager().getPropertyMap(property);
if (map == null) { if (map == null) {
return new WrappingCodeUnitIterator(Collections.emptyIterator()); return new WrappingCodeUnitIterator(Collections.emptyIterator());
} }
@ -380,7 +380,7 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
// TODO: Cover this in testing // TODO: Cover this in testing
TracePropertyMap<?> map = TracePropertyMap<?> map =
program.trace.getAddressPropertyManager().getPropertyMap(property); program.trace.getInternalAddressPropertyManager().getPropertyMap(property);
if (map == null) { if (map == null) {
return new WrappingCodeUnitIterator(Collections.emptyIterator()); return new WrappingCodeUnitIterator(Collections.emptyIterator());
} }
@ -403,7 +403,7 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
// TODO: Cover this in testing // TODO: Cover this in testing
TracePropertyMap<?> map = TracePropertyMap<?> map =
program.trace.getAddressPropertyManager().getPropertyMap(property); program.trace.getInternalAddressPropertyManager().getPropertyMap(property);
if (map == null) { if (map == null) {
return new WrappingCodeUnitIterator(Collections.emptyIterator()); return new WrappingCodeUnitIterator(Collections.emptyIterator());
} }

View file

@ -16,7 +16,8 @@
package ghidra.trace.database.property; package ghidra.trace.database.property;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReadWriteLock;
import db.DBHandle; import db.DBHandle;
@ -29,8 +30,7 @@ import ghidra.trace.database.DBTraceManager;
import ghidra.trace.database.map.AbstractDBTracePropertyMap; import ghidra.trace.database.map.AbstractDBTracePropertyMap;
import ghidra.trace.database.map.AbstractDBTracePropertyMap.*; import ghidra.trace.database.map.AbstractDBTracePropertyMap.*;
import ghidra.trace.database.thread.DBTraceThreadManager; import ghidra.trace.database.thread.DBTraceThreadManager;
import ghidra.trace.model.map.TracePropertyMap; import ghidra.trace.model.property.*;
import ghidra.trace.model.property.TraceAddressPropertyManager;
import ghidra.util.*; import ghidra.util.*;
import ghidra.util.database.*; import ghidra.util.database.*;
import ghidra.util.database.annot.*; import ghidra.util.database.annot.*;
@ -90,8 +90,9 @@ public class DBTraceAddressPropertyManager implements TraceAddressPropertyManage
protected final DBCachedObjectStore<DBTraceAddressPropertyEntry> propertyStore; protected final DBCachedObjectStore<DBTraceAddressPropertyEntry> propertyStore;
protected final Map<String, AbstractDBTracePropertyMap<?, ?>> propertyMapsByName = protected final Map<String, AbstractDBTracePropertyMap<?, ?>> propertyMapsByName =
new HashMap<>(); new HashMap<>();
protected final Map<String, TracePropertyMap<?>> propertyMapsView =
Collections.unmodifiableMap(propertyMapsByName); protected final TraceAddressPropertyManager apiView =
new DBTraceAddressPropertyManagerApiView(this);
public DBTraceAddressPropertyManager(DBHandle dbh, DBOpenMode openMode, ReadWriteLock lock, public DBTraceAddressPropertyManager(DBHandle dbh, DBOpenMode openMode, ReadWriteLock lock,
TaskMonitor monitor, Language baseLanguage, DBTrace trace, TaskMonitor monitor, Language baseLanguage, DBTrace trace,
@ -215,7 +216,7 @@ public class DBTraceAddressPropertyManager implements TraceAddressPropertyManage
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T> TracePropertyMap<? extends T> getPropertyGetter(String name, Class<T> valueClass) { public <T> TracePropertyGetter<T> getPropertyGetter(String name, Class<T> valueClass) {
try (LockHold hold = LockHold.lock(lock.readLock())) { try (LockHold hold = LockHold.lock(lock.readLock())) {
AbstractDBTracePropertyMap<?, ?> map = propertyMapsByName.get(name); AbstractDBTracePropertyMap<?, ?> map = propertyMapsByName.get(name);
if (map == null) { if (map == null) {
@ -225,19 +226,18 @@ public class DBTraceAddressPropertyManager implements TraceAddressPropertyManage
throw new TypeMismatchException("Property " + name + " has type " + throw new TypeMismatchException("Property " + name + " has type " +
map.getValueClass() + ", which does not extend " + valueClass); map.getValueClass() + ", which does not extend " + valueClass);
} }
return (TracePropertyMap<? extends T>) map; return (TracePropertyGetter<T>) map;
} }
} }
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T> TracePropertyMap<? super T> getOrCreatePropertySetter(String name, public <T> TracePropertySetter<T> getOrCreatePropertySetter(String name,
Class<T> valueClass) { Class<T> valueClass) {
try (LockHold hold = LockHold.lock(lock.writeLock())) { try (LockHold hold = LockHold.lock(lock.writeLock())) {
AbstractDBTracePropertyMap<?, ?> map = propertyMapsByName.get(name); AbstractDBTracePropertyMap<?, ?> map = propertyMapsByName.get(name);
if (map == null) { if (map == null) {
try { try {
// TODO: This is not ideal
return createPropertyMap(name, valueClass); return createPropertyMap(name, valueClass);
} }
catch (DuplicateNameException e) { catch (DuplicateNameException e) {
@ -261,7 +261,9 @@ public class DBTraceAddressPropertyManager implements TraceAddressPropertyManage
@Override @Override
public Map<String, TracePropertyMap<?>> getAllProperties() { public Map<String, TracePropertyMap<?>> getAllProperties() {
return propertyMapsView; try (LockHold hold = LockHold.lock(lock.readLock())) {
return Map.copyOf(propertyMapsByName);
}
} }
@Override @Override
@ -277,4 +279,8 @@ public class DBTraceAddressPropertyManager implements TraceAddressPropertyManage
loadPropertyMaps(TaskMonitor.DUMMY); loadPropertyMaps(TaskMonitor.DUMMY);
} }
} }
public TraceAddressPropertyManager getApiPropertyManager() {
return apiView;
}
} }

View file

@ -0,0 +1,76 @@
/* ###
* 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.property;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import ghidra.trace.model.property.*;
import ghidra.util.exception.DuplicateNameException;
class DBTraceAddressPropertyManagerApiView implements TraceAddressPropertyManager {
protected static final String API_PREFIX = "_API_";
protected final DBTraceAddressPropertyManager internalView;
public DBTraceAddressPropertyManagerApiView(DBTraceAddressPropertyManager internalView) {
this.internalView = internalView;
}
@Override
public <T> TracePropertyMap<T> createPropertyMap(String name, Class<T> valueClass)
throws DuplicateNameException {
return internalView.createPropertyMap(API_PREFIX + name, valueClass);
}
@Override
public <T> TracePropertyMap<T> getPropertyMap(String name, Class<T> valueClass) {
return internalView.getPropertyMap(API_PREFIX + name, valueClass);
}
@Override
public <T> TracePropertyMap<T> getOrCreatePropertyMap(String name, Class<T> valueClass) {
return internalView.getOrCreatePropertyMap(API_PREFIX + name, valueClass);
}
@Override
public <T> TracePropertyGetter<T> getPropertyGetter(String name,
Class<T> valueClass) {
return internalView.getPropertyGetter(API_PREFIX + name, valueClass);
}
@Override
public <T> TracePropertySetter<T> getOrCreatePropertySetter(String name,
Class<T> valueClass) {
return internalView.getOrCreatePropertySetter(API_PREFIX + name, valueClass);
}
@Override
public TracePropertyMap<?> getPropertyMap(String name) {
return internalView.getPropertyMap(API_PREFIX + name);
}
@Override
public Map<String, TracePropertyMap<?>> getAllProperties() {
return internalView.getAllProperties()
.entrySet()
.stream()
.filter(e -> e.getKey().startsWith(API_PREFIX))
.collect(Collectors.toMap(e -> e.getKey().substring(API_PREFIX.length()),
Entry::getValue));
}
}

View file

@ -38,6 +38,7 @@ import ghidra.trace.model.memory.*;
import ghidra.trace.model.modules.*; import ghidra.trace.model.modules.*;
import ghidra.trace.model.program.TraceProgramView; import ghidra.trace.model.program.TraceProgramView;
import ghidra.trace.model.program.TraceVariableSnapProgramView; import ghidra.trace.model.program.TraceVariableSnapProgramView;
import ghidra.trace.model.property.TraceAddressPropertyManager;
import ghidra.trace.model.stack.TraceStack; import ghidra.trace.model.stack.TraceStack;
import ghidra.trace.model.stack.TraceStackManager; import ghidra.trace.model.stack.TraceStackManager;
import ghidra.trace.model.symbol.*; import ghidra.trace.model.symbol.*;
@ -388,6 +389,8 @@ public interface Trace extends DataTypeManagerDomainObject {
AddressFactory getBaseAddressFactory(); AddressFactory getBaseAddressFactory();
TraceAddressPropertyManager getAddressPropertyManager();
TraceBookmarkManager getBookmarkManager(); TraceBookmarkManager getBookmarkManager();
TraceBreakpointManager getBreakpointManager(); TraceBreakpointManager getBreakpointManager();

View file

@ -20,7 +20,7 @@ import java.util.Map;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace; import ghidra.program.model.address.AddressSpace;
import ghidra.trace.model.map.UnsignedUtils; import ghidra.util.NumericUtilities;
import ghidra.util.database.spatial.rect.EuclideanSpace2D; import ghidra.util.database.spatial.rect.EuclideanSpace2D;
import utilities.util.IDHashed; import utilities.util.IDHashed;
@ -65,10 +65,10 @@ public class TraceAddressSnapSpace implements EuclideanSpace2D<Address, Long> {
@Override @Override
public double distX(Address x1, Address x2) { public double distX(Address x1, Address x2) {
if (x2.compareTo(x1) > 0) { if (x2.compareTo(x1) > 0) {
return UnsignedUtils.unsignedLongToDouble(x2.subtract(x1)); return NumericUtilities.unsignedLongToDouble(x2.subtract(x1));
} }
else { else {
return UnsignedUtils.unsignedLongToDouble(x1.subtract(x2)); return NumericUtilities.unsignedLongToDouble(x1.subtract(x2));
} }
} }

View file

@ -1,36 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.trace.model.map;
import com.google.common.primitives.UnsignedLong;
public enum UnsignedUtils {
;
/**
* Equivalent to {@link UnsignedLong#doubleValue()}, but without the overhead of instantiating
* one.
*
* @param val the long to treat as unsigned and convert
* @return the double
*/
public static double unsignedLongToDouble(long val) {
double dVal = val & 0x7FFF_FFFF_FFFF_FFFFL;
if (val < 0) {
dVal += 0x1.0p63;
}
return dVal;
}
}

View file

@ -17,17 +17,30 @@ package ghidra.trace.model.property;
import java.util.Map; import java.util.Map;
import com.google.common.collect.Range;
import ghidra.program.model.address.Address;
import ghidra.program.model.util.TypeMismatchException; import ghidra.program.model.util.TypeMismatchException;
import ghidra.trace.model.map.TracePropertyMap; import ghidra.util.Saveable;
import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.DuplicateNameException;
public interface TraceAddressPropertyManager { public interface TraceAddressPropertyManager {
/** /**
* Create a property map with the given name having the given type * Create a property map with the given name having the given type
* *
* <p>
* The following types are supported for valueClass:
* <ul>
* <li>{@link Integer}</li>
* <li>{@link Long}</li>
* <li>{@link String}</li>
* <li>{@link Void}: presence or absence of entry satisfies "boolean" use case</li>
* <li>{@code ? extends }{@link Saveable}</li>
* </ul>
*
* <p>
* Note that for maps of user-defined {@link Saveable} type, only the specified type is accepted
* by the map. Attempting to save an extension of that type may lead to undefined behavior,
* esp., if it attempts to save additional fields. When the value is restored, it will have the
* type given in {@code valueClass}, not the extended type.
*
* @param name the name * @param name the name
* @param valueClass the type of values * @param valueClass the type of values
* @return the new property map * @return the new property map
@ -49,6 +62,7 @@ public interface TraceAddressPropertyManager {
/** /**
* Get the property map with the given name, creating it if necessary, of the given type * Get the property map with the given name, creating it if necessary, of the given type
* *
* @see #createPropertyMap(String, Class)
* @param name the name * @param name the name
* @param valueClass the expected type of values * @param valueClass the expected type of values
* @return the (possibly new) property map * @return the (possibly new) property map
@ -58,38 +72,30 @@ public interface TraceAddressPropertyManager {
/** /**
* Get the property map with the given name, if its type extends the given type * Get the property map with the given name, if its type extends the given type
* *
* This implies that the returned map's {@link TracePropertyMap#get(long, Address)} method will
* return values which can be assigned to variables of the given type. Its
* {@link TracePropertyMap#set(Range, Address, Object)} method, however, will not accept any
* value parameter, as it will have a wildcard-extends type.
*
* @param name the name * @param name the name
* @param valueClass the expected type of values to get * @param valueClass the expected type of values to get
* @return the property map suitable for getting values of the given type * @return the property map suitable for getting values of the given type
*/ */
<T> TracePropertyMap<? extends T> getPropertyGetter(String name, Class<T> valueClass); <T> TracePropertyGetter<T> getPropertyGetter(String name, Class<T> valueClass);
/** /**
* Get the property map with the given name, if its type is a super-type of the given type * Get the property map with the given name, if its type is a super-type of the given type
* *
* This implies that the returned map's {@link TracePropertyMap#set(Range, Address, Object)} * @see #createPropertyMap(String, Class)
* method will accept values from variables of the given type. Its
* {@link TracePropertyMap#get(long, Address)} method, however, will return {@link Object}, as
* it will have a wildcard-super type.
*
* @param name the name * @param name the name
* @param valueClass the expect3ed type of values to set * @param valueClass the expected type of values to set
* @return the property map suitable for setting values of the given type * @return the property map suitable for setting values of the given type
*/ */
<T> TracePropertyMap<? super T> getOrCreatePropertySetter(String name, Class<T> valueClass); <T> TracePropertySetter<T> getOrCreatePropertySetter(String name, Class<T> valueClass);
/** /**
* Get the property map with the given name. * Get the property map with the given name.
* *
* Note that no type checking is performed (there is no {@code valueClass} parameter, after all. * <p>
* Thus, the returned map is suitable only for clearing and querying where the property is * Note that no type checking is performed (there is no {@code valueClass} parameter). Thus, the
* present. The caller may perform run-time type checking via the * returned map is suitable only for clearing and querying where the property is present. The
* {@link TracePropertyMap#getValueClass()} method. * caller may perform run-time type checking via the {@link TracePropertyMap#getValueClass()}
* method.
* *
* @param name the name * @param name the name
* @return the property map * @return the property map
@ -97,9 +103,9 @@ public interface TraceAddressPropertyManager {
TracePropertyMap<?> getPropertyMap(String name); TracePropertyMap<?> getPropertyMap(String name);
/** /**
* Get an unmodifiable view of all the defined properties * Get a copy of all the defined properties
* *
* @return the map view of names to property maps * @return the set of names
*/ */
Map<String, TracePropertyMap<?>> getAllProperties(); Map<String, TracePropertyMap<?>> getAllProperties();
} }

View file

@ -13,20 +13,35 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package ghidra.trace.model.map; package ghidra.trace.model.property;
import com.google.common.collect.Range; import com.google.common.collect.Range;
import ghidra.program.model.address.*; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView;
public interface TracePropertyMap<T> { public interface TracePropertyGetter<T> {
Class<T> getValueClass(); /**
* Get the class for values of the map
void set(Range<Long> lifespan, Address address, T value); *
* @return the value class
*/
Class<? extends T> getValueClass();
/**
* Get the value at the given address-snap pair
*
* @param snap the snap
* @param address the address
* @return the value
*/
T get(long snap, Address address); T get(long snap, Address address);
/**
* Get the union of address ranges for entries which intersect the given span
*
* @param span the range of snaps
* @return the address set
*/
AddressSetView getAddressSetView(Range<Long> span); AddressSetView getAddressSetView(Range<Long> span);
void clear(Range<Long> span, AddressRange range);
} }

View file

@ -0,0 +1,48 @@
/* ###
* 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.property;
import java.util.Map;
import ghidra.program.model.address.Address;
import ghidra.trace.model.Trace;
import ghidra.trace.model.TraceAddressSnapRange;
/**
* A map from address-snap pairs to user-defined values in a {@link Trace}
*/
public interface TracePropertyMap<T> extends TracePropertySetter<T>, TracePropertyGetter<T> {
/**
* Get the class for values of the map
*
* @return the value class
*/
@Override
Class<T> getValueClass();
/**
* Get the entry at the given address-snap pair
*
* <p>
* Because there exists {@link Map.Entry#setValue(Object)}, this method cannot be in
* {@link TracePropertyGetter}.
*
* @param snap the snap
* @param address the address
* @return the entry, which includes the ranges and the value
*/
Map.Entry<TraceAddressSnapRange, T> getEntry(long snap, Address address);
}

View file

@ -0,0 +1,72 @@
/* ###
* 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.property;
import com.google.common.collect.Range;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressRange;
public interface TracePropertySetter<T> {
/**
* Get the class for values of the map
*
* @return the value class
*/
Class<? super T> getValueClass();
/**
* Set a value at the given address over the given lifespan
*
* @see #set(Range, AddressRange, Object)
* @param lifespan the lifespan
* @param address the address
* @param value the value
*/
void set(Range<Long> lifespan, Address address, T value);
/**
* Set a value over the given ranges
*
* <p>
* Setting a value of null still creates an entry, so that Void-typed maps function.
*
* <p>
* When setting an overlapping value, existing entries are deleted or truncated to make space
* for the new entry. If an existing entry overlaps and its starting snap is contained in the
* new entry's span, the existing entry is deleted, regardless of whether or not its ending snap
* is also contained in the new entry's span. If the starting snap of the existing entry
* precedes the span of the new entry, the existing entry is truncated -- its ending snap is set
* to one less than the new entry's starting snap. Address ranges are never truncated.
*
* @param lifespan the lifespan
* @param range the address range
* @param value the value
*/
void set(Range<Long> lifespan, AddressRange range, T value);
/**
* Remove or truncate entries so that the given span and range has no values
*
* <p>
* This applies the same truncation rule as in {@link #set(Range, AddressRange, Object)}, except
* that no replacement entry is created.
*
* @param span the range of snaps
* @param range the address range
*/
void clear(Range<Long> span, AddressRange range);
}

View file

@ -30,7 +30,6 @@ import org.junit.*;
import db.DBHandle; import db.DBHandle;
import db.DBRecord; import db.DBRecord;
import ghidra.lifecycle.Unfinished;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.lang.Language; import ghidra.program.model.lang.Language;
import ghidra.program.model.lang.LanguageID; import ghidra.program.model.lang.LanguageID;
@ -55,7 +54,7 @@ import ghidra.util.task.ConsoleTaskMonitor;
* *
*/ */
public class DBTraceAddressSnapRangePropertyMapSpaceTest public class DBTraceAddressSnapRangePropertyMapSpaceTest
extends AbstractGhidraHeadlessIntegrationTest implements Unfinished { extends AbstractGhidraHeadlessIntegrationTest {
protected static class MyObject extends DBCachedDomainObjectAdapter implements AutoCloseable { protected static class MyObject extends DBCachedDomainObjectAdapter implements AutoCloseable {
private final DBCachedObjectStoreFactory factory; private final DBCachedObjectStoreFactory factory;
private final Language toy; private final Language toy;

View file

@ -0,0 +1,378 @@
/* ###
* 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.property;
import static org.junit.Assert.*;
import java.io.File;
import java.util.Map.Entry;
import java.util.Objects;
import org.junit.*;
import com.google.common.collect.Range;
import ghidra.program.model.util.TypeMismatchException;
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
import ghidra.trace.database.ToyDBTraceBuilder;
import ghidra.trace.model.TraceAddressSnapRange;
import ghidra.trace.model.property.*;
import ghidra.util.ObjectStorage;
import ghidra.util.Saveable;
import ghidra.util.database.UndoableTransaction;
import ghidra.util.exception.DuplicateNameException;
public class DBTraceAddressPropertyManagerTest extends AbstractGhidraHeadlessIntegrationTest {
protected static class MySaveable implements Saveable {
protected int i;
protected String str;
public MySaveable() {
}
public MySaveable(int i, String str) {
this.i = i;
this.str = str;
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof MySaveable)) {
return false;
}
MySaveable that = (MySaveable) obj;
return this.i == that.i && Objects.equals(this.str, that.str);
}
@Override
public int hashCode() {
return Objects.hash(i, str);
}
@Override
public Class<?>[] getObjectStorageFields() {
return new Class[] { Integer.class, String.class };
}
@Override
public void save(ObjectStorage objStorage) {
objStorage.putInt(i);
objStorage.putString(str);
}
@Override
public void restore(ObjectStorage objStorage) {
i = objStorage.getInt();
str = objStorage.getString();
}
@Override
public int getSchemaVersion() {
return 0;
}
@Override
public boolean isUpgradeable(int oldSchemaVersion) {
return false;
}
@Override
public boolean upgrade(ObjectStorage oldObjStorage, int oldSchemaVersion,
ObjectStorage currentObjStorage) {
return false;
}
@Override
public boolean isPrivate() {
return false;
}
}
protected static class ExtMySaveable extends MySaveable {
private float f;
public ExtMySaveable() {
}
public ExtMySaveable(int i, String str, float f) {
super(i, str);
this.f = f;
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof ExtMySaveable)) {
return false;
}
ExtMySaveable that = (ExtMySaveable) obj;
return super.equals(that) && this.f == that.f;
}
@Override
public int hashCode() {
return Objects.hash(this.i, this.str, this.f);
}
@Override
public Class<?>[] getObjectStorageFields() {
return new Class[] { Integer.class, String.class, Float.class };
}
@Override
public void save(ObjectStorage objStorage) {
super.save(objStorage);
objStorage.putFloat(f);
}
@Override
public void restore(ObjectStorage objStorage) {
super.restore(objStorage);
f = objStorage.getFloat();
}
}
protected static class Unsupported {
}
ToyDBTraceBuilder tb;
TraceAddressPropertyManager propertyManager;
@Before
public void setUpPropertyManagerTest() throws Exception {
tb = new ToyDBTraceBuilder("Testing", "Toy:BE:64:default");
propertyManager = tb.trace.getAddressPropertyManager();
}
@After
public void tearDownPropertyManagerTest() throws Exception {
tb.close();
}
protected void doTestCreatePropertyMap(Class<?> valueClass) throws Exception {
try (UndoableTransaction tid = tb.startTransaction()) {
propertyManager.createPropertyMap("MyProp", valueClass);
}
try (UndoableTransaction tid = tb.startTransaction()) {
propertyManager.createPropertyMap("MyProp", valueClass);
fail();
}
catch (DuplicateNameException e) {
// pass
}
}
@Test
public void testCreateIntegerPropertyMap() throws Exception {
doTestCreatePropertyMap(Integer.class);
}
@Test
public void testCreateLongPropertyMap() throws Exception {
doTestCreatePropertyMap(Long.class);
}
@Test
public void testCreateStringPropertyMap() throws Exception {
doTestCreatePropertyMap(String.class);
}
@Test
public void testCreateVoidPropertyMap() throws Exception {
doTestCreatePropertyMap(Void.class);
}
@Test
public void testCreateSaveablePropertyMap() throws Exception {
doTestCreatePropertyMap(MySaveable.class);
}
@Test(expected = IllegalArgumentException.class)
public void testCreateUnsupportedPropertyMap() throws Exception {
try (UndoableTransaction tid = tb.startTransaction()) {
propertyManager.createPropertyMap("MyProp", Unsupported.class);
}
}
@Test
public void testGetPropertyMap() throws Exception {
assertNull(propertyManager.getPropertyMap("MyProp"));
TracePropertyMap<String> map;
try (UndoableTransaction tid = tb.startTransaction()) {
map = propertyManager.createPropertyMap("MyProp", String.class);
}
assertNotNull(map);
assertSame(map, propertyManager.getPropertyMap("MyProp"));
assertSame(map, propertyManager.getPropertyMap("MyProp", String.class));
try {
propertyManager.getPropertyMap("MyProp", Integer.class);
fail();
}
catch (TypeMismatchException e) {
// pass
}
}
@Test
public void testGetOrCreatePropertyMap() throws Exception {
assertNull(propertyManager.getPropertyMap("MyProp"));
TracePropertyMap<String> map;
try (UndoableTransaction tid = tb.startTransaction()) {
map = propertyManager.getOrCreatePropertyMap("MyProp", String.class);
}
assertNotNull(map);
assertSame(map, propertyManager.getOrCreatePropertyMap("MyProp", String.class));
try {
propertyManager.getOrCreatePropertyMap("MyProp", Integer.class);
fail();
}
catch (TypeMismatchException e) {
// pass
}
}
@Test
public void testGetPropertyGetter() throws Exception {
assertNull(propertyManager.getPropertyGetter("MyProp", String.class));
TracePropertyMap<String> map;
try (UndoableTransaction tid = tb.startTransaction()) {
map = propertyManager.createPropertyMap("MyProp", String.class);
}
assertNotNull(map);
TracePropertyGetter<String> getter =
propertyManager.getPropertyGetter("MyProp", String.class);
assertSame(map, getter);
assertSame(map, propertyManager.getPropertyGetter("MyProp", Object.class));
try {
propertyManager.getPropertyGetter("MyProp", Integer.class);
fail();
}
catch (TypeMismatchException e) {
// pass
}
}
@Test
public void testGetOrCreatePropertySetter() throws Exception {
TracePropertyMap<MySaveable> map;
try (UndoableTransaction tid = tb.startTransaction()) {
map = propertyManager.createPropertyMap("MyProp", MySaveable.class);
}
assertNotNull(map);
TracePropertySetter<ExtMySaveable> setter =
propertyManager.getOrCreatePropertySetter("MyProp", ExtMySaveable.class);
assertSame(map, setter);
assertSame(map, propertyManager.getOrCreatePropertySetter("MyProp", MySaveable.class));
try {
propertyManager.getOrCreatePropertySetter("MyProp", Saveable.class);
fail();
}
catch (TypeMismatchException e) {
// pass
}
}
@Test
public void testGetAllProperties() throws Exception {
assertEquals(0, propertyManager.getAllProperties().size());
TracePropertyMap<String> map;
try (UndoableTransaction tid = tb.startTransaction()) {
map = propertyManager.createPropertyMap("MyProp", String.class);
}
assertNotNull(map);
assertEquals(1, propertyManager.getAllProperties().size());
assertSame(map, propertyManager.getAllProperties().get("MyProp"));
}
@Test
public void testMapGetValueClass() throws Exception {
TracePropertyMap<String> map;
try (UndoableTransaction tid = tb.startTransaction()) {
map = propertyManager.createPropertyMap("MyProp", String.class);
}
assertSame(String.class, map.getValueClass());
}
protected <T> void doTestMap(Class<T> valueClass, T value) throws Exception {
try (UndoableTransaction tid = tb.startTransaction()) {
TracePropertyMap<T> map =
propertyManager.createPropertyMap("MyProp", valueClass);
assertSame(valueClass, map.getValueClass());
map.set(Range.atLeast(0L), tb.range(0x00400000, 0x00400003), value);
assertEquals(value, map.get(4, tb.addr(0x00400001)));
assertEquals(tb.set(tb.range(0x00400000, 0x00400003)),
map.getAddressSetView(Range.singleton(0L)));
Entry<TraceAddressSnapRange, T> entry = map.getEntry(4, tb.addr(0x00400001));
assertEquals(value, entry.getValue());
assertEquals(Range.atLeast(0L), entry.getKey().getLifespan());
assertEquals(tb.range(0x00400000, 0x00400003), entry.getKey().getRange());
map.clear(Range.atLeast(11L), tb.range(0x00400000));
assertEquals(value, map.get(4, tb.addr(0x00400001)));
assertNull(map.get(11, tb.addr(0x00400001)));
}
File file = tb.save();
try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder(file)) {
TraceAddressPropertyManager propertyManager = tb.trace.getAddressPropertyManager();
TracePropertyMap<T> map = propertyManager.getPropertyMap("MyProp", valueClass);
assertNotNull(map);
Entry<TraceAddressSnapRange, T> entry = map.getEntry(4, tb.addr(0x00400001));
assertEquals(value, entry.getValue());
assertEquals(Range.closed(0L, 10L), entry.getKey().getLifespan());
assertEquals(tb.range(0x00400000, 0x00400003), entry.getKey().getRange());
}
}
@Test
public void testIntegerMap() throws Exception {
doTestMap(Integer.class, 6);
}
@Test
public void testLongMap() throws Exception {
doTestMap(Long.class, 6L);
}
@Test
public void testStringMap() throws Exception {
doTestMap(String.class, "MyString");
}
@Test
public void testVoidMap() throws Exception {
doTestMap(Void.class, null);
}
@Test
public void testSaveableMap() throws Exception {
doTestMap(MySaveable.class, new MySaveable(6, "MyString"));
}
}

View file

@ -59,6 +59,7 @@ public final class NumericUtilities {
/** /**
* Parses the given string as a numeric value, detecting whether or not it begins with a Hex * Parses the given string as a numeric value, detecting whether or not it begins with a Hex
* prefix, and if not, parses as a long int value. * prefix, and if not, parses as a long int value.
*
* @param numStr the number string * @param numStr the number string
* @return the long value or 0 * @return the long value or 0
* *
@ -238,15 +239,15 @@ public final class NumericUtilities {
} }
/** /**
* Converts a <strong>unsigned</strong> long value, which is currently stored in a * Converts a <strong>unsigned</strong> long value, which is currently stored in a java
* java <strong>signed</strong> long, into a {@link BigInteger}. * <strong>signed</strong> long, into a {@link BigInteger}.
* <p> * <p>
* In other words, the full 64 bits of the primitive java <strong>signed</strong> * In other words, the full 64 bits of the primitive java <strong>signed</strong> long is being
* long is being used to store an <strong>unsigned</strong> value. This * used to store an <strong>unsigned</strong> value. This method converts this into a positive
* method converts this into a positive BigInteger value. * BigInteger value.
* *
* @param value java <strong>unsigned</strong> long value stuffed into a * @param value java <strong>unsigned</strong> long value stuffed into a java
* java <strong>signed</strong> long * <strong>signed</strong> long
* @return new {@link BigInteger} with the positive value of the unsigned long value * @return new {@link BigInteger} with the positive value of the unsigned long value
*/ */
public static BigInteger unsignedLongToBigInteger(long value) { public static BigInteger unsignedLongToBigInteger(long value) {
@ -263,6 +264,20 @@ public final class NumericUtilities {
return value.subtract(MAX_UNSIGNED_LONG).longValue() - 1; return value.subtract(MAX_UNSIGNED_LONG).longValue() - 1;
} }
/**
* Convert a long, treated as unsigned, to a double
*
* @param val the long to treat as unsigned and convert
* @return the double
*/
public static double unsignedLongToDouble(long val) {
double dVal = val & 0x7FFF_FFFF_FFFF_FFFFL;
if (val < 0) {
dVal += 0x1.0p63;
}
return dVal;
}
/** /**
* Get an unsigned aligned value corresponding to the specified unsigned value which will be * Get an unsigned aligned value corresponding to the specified unsigned value which will be
* greater than or equal the specified value. * greater than or equal the specified value.