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

View file

@ -33,9 +33,9 @@ import ghidra.program.model.symbol.*;
import ghidra.trace.database.DBTrace;
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.property.*;
import ghidra.trace.model.symbol.TraceReference;
import ghidra.trace.model.symbol.TraceSymbol;
import ghidra.trace.model.thread.TraceThread;
@ -84,8 +84,9 @@ public interface DBTraceCodeUnitAdapter extends TraceCodeUnit, MemBufferAdapter
@Override
default <T> void setProperty(String name, Class<T> valueClass, T value) {
try (LockHold hold = LockHold.lock(getTrace().getReadWriteLock().writeLock())) {
TracePropertyMap<? super T> setter =
getTrace().getAddressPropertyManager().getOrCreatePropertySetter(name, valueClass);
TracePropertySetter<T> setter =
getTrace().getInternalAddressPropertyManager()
.getOrCreatePropertySetter(name, valueClass);
setter.set(getLifespan(), getAddress(), value);
}
}
@ -121,8 +122,8 @@ public interface DBTraceCodeUnitAdapter extends TraceCodeUnit, MemBufferAdapter
@Override
default <T> T getProperty(String name, Class<T> valueClass) {
try (LockHold hold = LockHold.lock(getTrace().getReadWriteLock().readLock())) {
TracePropertyMap<? extends T> getter =
getTrace().getAddressPropertyManager().getPropertyGetter(name, valueClass);
TracePropertyGetter<T> getter =
getTrace().getInternalAddressPropertyManager().getPropertyGetter(name, valueClass);
return getter.get(getStartSnap(), getAddress());
}
}
@ -149,13 +150,13 @@ public interface DBTraceCodeUnitAdapter extends TraceCodeUnit, MemBufferAdapter
@Override
default boolean hasProperty(String name) {
try (LockHold hold = LockHold.lock(getTrace().getReadWriteLock().readLock())) {
TracePropertyMap<?> map = getTrace().getAddressPropertyManager().getPropertyMap(name);
TracePropertyMap<?> map =
getTrace().getInternalAddressPropertyManager().getPropertyMap(name);
if (map == null) {
return false;
}
// NOTE: Properties all defined at start snap
return map.getAddressSetView(Range.closed(getStartSnap(), getStartSnap()))
.contains(getAddress());
return map.getAddressSetView(Range.singleton(getStartSnap())).contains(getAddress());
}
}
@ -163,29 +164,28 @@ public interface DBTraceCodeUnitAdapter extends TraceCodeUnit, MemBufferAdapter
default boolean getVoidProperty(String name) {
// NOTE: Nearly identical to hasProperty, except named property must be Void type
try (LockHold hold = LockHold.lock(getTrace().getReadWriteLock().readLock())) {
TracePropertyMap<? extends Void> getter =
getTrace().getAddressPropertyManager().getPropertyGetter(name, Void.class);
TracePropertyGetter<Void> getter =
getTrace().getInternalAddressPropertyManager().getPropertyGetter(name, Void.class);
if (getter == null) {
return false;
}
return getter.getAddressSetView(Range.closed(getStartSnap(), getStartSnap()))
.contains(
getAddress());
return getter.getAddressSetView(Range.singleton(getStartSnap())).contains(getAddress());
}
}
@Override
default Iterator<String> propertyNames() {
Range<Long> closed = Range.closed(getStartSnap(), getStartSnap());
Range<Long> span = Range.singleton(getStartSnap());
return Iterators.transform(Iterators.filter(
getTrace().getAddressPropertyManager().getAllProperties().entrySet().iterator(),
e -> e.getValue().getAddressSetView(closed).contains(getAddress())), Entry::getKey);
getTrace().getInternalAddressPropertyManager().getAllProperties().entrySet().iterator(),
e -> e.getValue().getAddressSetView(span).contains(getAddress())), Entry::getKey);
}
@Override
default void removeProperty(String name) {
try (LockHold hold = LockHold.lock(getTrace().getReadWriteLock().writeLock())) {
TracePropertyMap<?> map = getTrace().getAddressPropertyManager().getPropertyMap(name);
TracePropertyMap<?> map =
getTrace().getInternalAddressPropertyManager().getPropertyMap(name);
if (map == null) {
return;
}
@ -197,14 +197,12 @@ public interface DBTraceCodeUnitAdapter extends TraceCodeUnit, MemBufferAdapter
default void visitProperty(PropertyVisitor visitor, String propertyName) {
try (LockHold hold = LockHold.lock(getTrace().getReadWriteLock().readLock())) {
TracePropertyMap<?> map =
getTrace().getAddressPropertyManager().getPropertyMap(propertyName);
getTrace().getInternalAddressPropertyManager().getPropertyMap(propertyName);
if (map == null) {
return;
}
if (map.getValueClass() == Void.class) {
if (map.getAddressSetView(Range.closed(getStartSnap(), getStartSnap()))
.contains(
getAddress())) {
if (map.getAddressSetView(Range.singleton(getStartSnap())).contains(getAddress())) {
visitor.visit();
}
return;
@ -437,5 +435,4 @@ public interface DBTraceCodeUnitAdapter extends TraceCodeUnit, MemBufferAdapter
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.thread.DBTraceThreadManager;
import ghidra.trace.model.TraceAddressSnapRange;
import ghidra.trace.model.map.TracePropertyMap;
import ghidra.trace.model.property.TracePropertyMap;
import ghidra.util.*;
import ghidra.util.database.*;
import ghidra.util.database.DBCachedObjectStoreFactory.AbstractDBFieldCodec;
@ -71,11 +71,21 @@ public abstract class AbstractDBTracePropertyMap<T, DR extends AbstractDBTraceAd
put(address, lifespan, value);
}
@Override
public void set(Range<Long> lifespan, AddressRange range, T value) {
put(range, lifespan, value);
}
@Override
public T get(long snap, Address address) {
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
public void clear(Range<Long> span, AddressRange range) {
try (LockHold hold = LockHold.lock(lock.writeLock())) {
@ -88,7 +98,6 @@ public abstract class AbstractDBTracePropertyMap<T, DR extends AbstractDBTraceAd
@Override
public T put(TraceAddressSnapRange shape, T value) {
assert shape.getRange().getLength() == 1;
try (LockHold hold = LockHold.lock(lock.writeLock())) {
for (Entry<TraceAddressSnapRange, T> entry : reduce(
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.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.property.TracePropertyMap;
import ghidra.trace.model.symbol.TraceFunctionSymbol;
import ghidra.trace.util.*;
import ghidra.util.*;
@ -358,7 +358,7 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
// TODO: Cover this in testing
TracePropertyMap<?> map =
program.trace.getAddressPropertyManager().getPropertyMap(property);
program.trace.getInternalAddressPropertyManager().getPropertyMap(property);
if (map == null) {
return new WrappingCodeUnitIterator(Collections.emptyIterator());
}
@ -380,7 +380,7 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
// TODO: Cover this in testing
TracePropertyMap<?> map =
program.trace.getAddressPropertyManager().getPropertyMap(property);
program.trace.getInternalAddressPropertyManager().getPropertyMap(property);
if (map == null) {
return new WrappingCodeUnitIterator(Collections.emptyIterator());
}
@ -403,7 +403,7 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
// TODO: Cover this in testing
TracePropertyMap<?> map =
program.trace.getAddressPropertyManager().getPropertyMap(property);
program.trace.getInternalAddressPropertyManager().getPropertyMap(property);
if (map == null) {
return new WrappingCodeUnitIterator(Collections.emptyIterator());
}

View file

@ -16,7 +16,8 @@
package ghidra.trace.database.property;
import java.io.IOException;
import java.util.*;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
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.thread.DBTraceThreadManager;
import ghidra.trace.model.map.TracePropertyMap;
import ghidra.trace.model.property.TraceAddressPropertyManager;
import ghidra.trace.model.property.*;
import ghidra.util.*;
import ghidra.util.database.*;
import ghidra.util.database.annot.*;
@ -90,8 +90,9 @@ public class DBTraceAddressPropertyManager implements TraceAddressPropertyManage
protected final DBCachedObjectStore<DBTraceAddressPropertyEntry> propertyStore;
protected final Map<String, AbstractDBTracePropertyMap<?, ?>> propertyMapsByName =
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,
TaskMonitor monitor, Language baseLanguage, DBTrace trace,
@ -215,7 +216,7 @@ public class DBTraceAddressPropertyManager implements TraceAddressPropertyManage
@Override
@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())) {
AbstractDBTracePropertyMap<?, ?> map = propertyMapsByName.get(name);
if (map == null) {
@ -225,19 +226,18 @@ public class DBTraceAddressPropertyManager implements TraceAddressPropertyManage
throw new TypeMismatchException("Property " + name + " has type " +
map.getValueClass() + ", which does not extend " + valueClass);
}
return (TracePropertyMap<? extends T>) map;
return (TracePropertyGetter<T>) map;
}
}
@Override
@SuppressWarnings("unchecked")
public <T> TracePropertyMap<? super T> getOrCreatePropertySetter(String name,
public <T> TracePropertySetter<T> getOrCreatePropertySetter(String name,
Class<T> valueClass) {
try (LockHold hold = LockHold.lock(lock.writeLock())) {
AbstractDBTracePropertyMap<?, ?> map = propertyMapsByName.get(name);
if (map == null) {
try {
// TODO: This is not ideal
return createPropertyMap(name, valueClass);
}
catch (DuplicateNameException e) {
@ -261,7 +261,9 @@ public class DBTraceAddressPropertyManager implements TraceAddressPropertyManage
@Override
public Map<String, TracePropertyMap<?>> getAllProperties() {
return propertyMapsView;
try (LockHold hold = LockHold.lock(lock.readLock())) {
return Map.copyOf(propertyMapsByName);
}
}
@Override
@ -277,4 +279,8 @@ public class DBTraceAddressPropertyManager implements TraceAddressPropertyManage
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.program.TraceProgramView;
import ghidra.trace.model.program.TraceVariableSnapProgramView;
import ghidra.trace.model.property.TraceAddressPropertyManager;
import ghidra.trace.model.stack.TraceStack;
import ghidra.trace.model.stack.TraceStackManager;
import ghidra.trace.model.symbol.*;
@ -388,6 +389,8 @@ public interface Trace extends DataTypeManagerDomainObject {
AddressFactory getBaseAddressFactory();
TraceAddressPropertyManager getAddressPropertyManager();
TraceBookmarkManager getBookmarkManager();
TraceBreakpointManager getBreakpointManager();

View file

@ -20,7 +20,7 @@ import java.util.Map;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.trace.model.map.UnsignedUtils;
import ghidra.util.NumericUtilities;
import ghidra.util.database.spatial.rect.EuclideanSpace2D;
import utilities.util.IDHashed;
@ -65,10 +65,10 @@ public class TraceAddressSnapSpace implements EuclideanSpace2D<Address, Long> {
@Override
public double distX(Address x1, Address x2) {
if (x2.compareTo(x1) > 0) {
return UnsignedUtils.unsignedLongToDouble(x2.subtract(x1));
return NumericUtilities.unsignedLongToDouble(x2.subtract(x1));
}
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 com.google.common.collect.Range;
import ghidra.program.model.address.Address;
import ghidra.program.model.util.TypeMismatchException;
import ghidra.trace.model.map.TracePropertyMap;
import ghidra.util.Saveable;
import ghidra.util.exception.DuplicateNameException;
public interface TraceAddressPropertyManager {
/**
* 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 valueClass the type of values
* @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
*
* @see #createPropertyMap(String, Class)
* @param name the name
* @param valueClass the expected type of values
* @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
*
* 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 valueClass the expected type of values to get
* @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
*
* This implies that the returned map's {@link TracePropertyMap#set(Range, Address, Object)}
* 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.
*
* @see #createPropertyMap(String, Class)
* @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
*/
<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.
*
* Note that no type checking is performed (there is no {@code valueClass} parameter, after all.
* Thus, the returned map is suitable only for clearing and querying where the property is
* present. The caller may perform run-time type checking via the
* {@link TracePropertyMap#getValueClass()} method.
* <p>
* Note that no type checking is performed (there is no {@code valueClass} parameter). Thus, the
* returned map is suitable only for clearing and querying where the property is present. The
* caller may perform run-time type checking via the {@link TracePropertyMap#getValueClass()}
* method.
*
* @param name the name
* @return the property map
@ -97,9 +103,9 @@ public interface TraceAddressPropertyManager {
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();
}

View file

@ -13,20 +13,35 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.trace.model.map;
package ghidra.trace.model.property;
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> {
Class<T> getValueClass();
void set(Range<Long> lifespan, Address address, T value);
public interface TracePropertyGetter<T> {
/**
* Get the class for values of the map
*
* @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);
/**
* 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);
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.DBRecord;
import ghidra.lifecycle.Unfinished;
import ghidra.program.model.address.Address;
import ghidra.program.model.lang.Language;
import ghidra.program.model.lang.LanguageID;
@ -55,7 +54,7 @@ import ghidra.util.task.ConsoleTaskMonitor;
*
*/
public class DBTraceAddressSnapRangePropertyMapSpaceTest
extends AbstractGhidraHeadlessIntegrationTest implements Unfinished {
extends AbstractGhidraHeadlessIntegrationTest {
protected static class MyObject extends DBCachedDomainObjectAdapter implements AutoCloseable {
private final DBCachedObjectStoreFactory factory;
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
* prefix, and if not, parses as a long int value.
*
* @param numStr the number string
* @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
* java <strong>signed</strong> long, into a {@link BigInteger}.
* Converts a <strong>unsigned</strong> long value, which is currently stored in a java
* <strong>signed</strong> long, into a {@link BigInteger}.
* <p>
* In other words, the full 64 bits of the primitive java <strong>signed</strong>
* long is being used to store an <strong>unsigned</strong> value. This
* method converts this into a positive BigInteger value.
* In other words, the full 64 bits of the primitive java <strong>signed</strong> long is being
* used to store an <strong>unsigned</strong> value. This method converts this into a positive
* BigInteger value.
*
* @param value java <strong>unsigned</strong> long value stuffed into a
* java <strong>signed</strong> long
* @param value java <strong>unsigned</strong> long value stuffed into a java
* <strong>signed</strong> long
* @return new {@link BigInteger} with the positive value of the unsigned long value
*/
public static BigInteger unsignedLongToBigInteger(long value) {
@ -263,6 +264,20 @@ public final class NumericUtilities {
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
* greater than or equal the specified value.