mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 18:29:37 +02:00
GP-4143: Add schema attribute aliases.
This commit is contained in:
parent
bd94f6579b
commit
d176347330
21 changed files with 584 additions and 158 deletions
|
@ -40,9 +40,22 @@ import ghidra.util.exception.DuplicateNameException;
|
|||
public class DBTraceObjectBreakpointLocation
|
||||
implements TraceObjectBreakpointLocation, DBTraceObjectInterface {
|
||||
|
||||
protected class BreakpointChangeTranslator extends Translator<TraceBreakpoint> {
|
||||
protected static class BreakpointChangeTranslator extends Translator<TraceBreakpoint> {
|
||||
private static final Map<TargetObjectSchema, Set<String>> KEYS_BY_SCHEMA =
|
||||
new WeakHashMap<>();
|
||||
|
||||
private final Set<String> keys;
|
||||
|
||||
protected BreakpointChangeTranslator(DBTraceObject object, TraceBreakpoint iface) {
|
||||
super(TargetBreakpointLocation.RANGE_ATTRIBUTE_NAME, object, iface);
|
||||
TargetObjectSchema schema = object.getTargetSchema();
|
||||
synchronized (KEYS_BY_SCHEMA) {
|
||||
keys = KEYS_BY_SCHEMA.computeIfAbsent(schema, s -> Set.of(
|
||||
schema.checkAliasedAttribute(TargetBreakpointLocation.RANGE_ATTRIBUTE_NAME),
|
||||
schema.checkAliasedAttribute(TargetObject.DISPLAY_ATTRIBUTE_NAME),
|
||||
schema.checkAliasedAttribute(TargetTogglable.ENABLED_ATTRIBUTE_NAME),
|
||||
schema.checkAliasedAttribute(KEY_COMMENT)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -62,10 +75,7 @@ public class DBTraceObjectBreakpointLocation
|
|||
|
||||
@Override
|
||||
protected boolean appliesToKey(String key) {
|
||||
return TargetBreakpointLocation.RANGE_ATTRIBUTE_NAME.equals(key) ||
|
||||
TargetObject.DISPLAY_ATTRIBUTE_NAME.equals(key) ||
|
||||
TargetBreakpointSpec.ENABLED_ATTRIBUTE_NAME.equals(key) ||
|
||||
KEY_COMMENT.equals(key);
|
||||
return keys.contains(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -15,12 +15,11 @@
|
|||
*/
|
||||
package ghidra.trace.database.breakpoint;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import ghidra.dbg.target.TargetBreakpointSpec;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.dbg.target.schema.TargetObjectSchema;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.trace.database.target.DBTraceObject;
|
||||
|
@ -44,12 +43,21 @@ import ghidra.util.exception.DuplicateNameException;
|
|||
|
||||
public class DBTraceObjectBreakpointSpec
|
||||
implements TraceObjectBreakpointSpec, DBTraceObjectInterface {
|
||||
private static final Map<TargetObjectSchema, Set<String>> KEYS_BY_SCHEMA = new WeakHashMap<>();
|
||||
|
||||
private final DBTraceObject object;
|
||||
private final Set<String> keys;
|
||||
|
||||
private TraceBreakpointKindSet kinds = TraceBreakpointKindSet.of();
|
||||
|
||||
public DBTraceObjectBreakpointSpec(DBTraceObject object) {
|
||||
this.object = object;
|
||||
TargetObjectSchema schema = object.getTargetSchema();
|
||||
synchronized (KEYS_BY_SCHEMA) {
|
||||
keys = KEYS_BY_SCHEMA.computeIfAbsent(schema, s -> Set.of(
|
||||
schema.checkAliasedAttribute(TargetBreakpointSpec.KINDS_ATTRIBUTE_NAME),
|
||||
schema.checkAliasedAttribute(TargetTogglable.ENABLED_ATTRIBUTE_NAME)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -238,8 +246,7 @@ public class DBTraceObjectBreakpointSpec
|
|||
TraceObjectChangeType.VALUE_CREATED.cast(rec);
|
||||
TraceObjectValue affected = cast.getAffectedObject();
|
||||
String key = affected.getEntryKey();
|
||||
boolean applies = TargetBreakpointSpec.KINDS_ATTRIBUTE_NAME.equals(key) ||
|
||||
TargetBreakpointSpec.ENABLED_ATTRIBUTE_NAME.equals(key);
|
||||
boolean applies = keys.contains(key);
|
||||
if (!applies) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import java.util.*;
|
|||
|
||||
import ghidra.dbg.target.TargetMemoryRegion;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.dbg.target.schema.TargetObjectSchema;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.trace.database.DBTrace;
|
||||
import ghidra.trace.database.DBTraceUtils;
|
||||
|
@ -38,9 +39,46 @@ import ghidra.util.exception.DuplicateNameException;
|
|||
|
||||
public class DBTraceObjectMemoryRegion implements TraceObjectMemoryRegion, DBTraceObjectInterface {
|
||||
|
||||
protected record Keys(Set<String> all, String range, String display,
|
||||
Set<String> flags) {
|
||||
static Keys fromSchema(TargetObjectSchema schema) {
|
||||
String keyRange = schema.checkAliasedAttribute(TargetMemoryRegion.RANGE_ATTRIBUTE_NAME);
|
||||
String keyDisplay = schema.checkAliasedAttribute(TargetObject.DISPLAY_ATTRIBUTE_NAME);
|
||||
String keyReadable =
|
||||
schema.checkAliasedAttribute(TargetMemoryRegion.READABLE_ATTRIBUTE_NAME);
|
||||
String keyWritable =
|
||||
schema.checkAliasedAttribute(TargetMemoryRegion.WRITABLE_ATTRIBUTE_NAME);
|
||||
String keyExecutable =
|
||||
schema.checkAliasedAttribute(TargetMemoryRegion.EXECUTABLE_ATTRIBUTE_NAME);
|
||||
return new Keys(Set.of(keyRange, keyDisplay, keyReadable, keyWritable, keyExecutable),
|
||||
keyRange, keyDisplay, Set.of(keyReadable, keyWritable, keyExecutable));
|
||||
}
|
||||
|
||||
public boolean isRange(String key) {
|
||||
return range.equals(key);
|
||||
}
|
||||
|
||||
public boolean isDisplay(String key) {
|
||||
return display.equals(key);
|
||||
}
|
||||
|
||||
public boolean isFlag(String key) {
|
||||
return flags.contains(key);
|
||||
}
|
||||
}
|
||||
|
||||
protected class RegionChangeTranslator extends Translator<TraceMemoryRegion> {
|
||||
private static final Map<TargetObjectSchema, Keys> KEYS_BY_SCHEMA =
|
||||
new WeakHashMap<>();
|
||||
|
||||
private final Keys keys;
|
||||
|
||||
protected RegionChangeTranslator(DBTraceObject object, TraceMemoryRegion iface) {
|
||||
super(TargetMemoryRegion.RANGE_ATTRIBUTE_NAME, object, iface);
|
||||
TargetObjectSchema schema = object.getTargetSchema();
|
||||
synchronized (KEYS_BY_SCHEMA) {
|
||||
keys = KEYS_BY_SCHEMA.computeIfAbsent(schema, Keys::fromSchema);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -60,11 +98,7 @@ public class DBTraceObjectMemoryRegion implements TraceObjectMemoryRegion, DBTra
|
|||
|
||||
@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);
|
||||
return keys.all.contains(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -375,19 +409,15 @@ public class DBTraceObjectMemoryRegion implements TraceObjectMemoryRegion, DBTra
|
|||
protected void updateViewsValueChanged(Lifespan lifespan, String key, Object oldValue,
|
||||
Object newValue) {
|
||||
DBTrace trace = object.getTrace();
|
||||
switch (key) {
|
||||
case TargetMemoryRegion.RANGE_ATTRIBUTE_NAME:
|
||||
// NB. old/newValue are null here. The CREATED event just has the new entry.
|
||||
trace.updateViewsRefreshBlocks();
|
||||
return;
|
||||
case TargetObject.DISPLAY_ATTRIBUTE_NAME:
|
||||
trace.updateViewsChangeRegionBlockName(this);
|
||||
return;
|
||||
case TargetMemoryRegion.READABLE_ATTRIBUTE_NAME:
|
||||
case TargetMemoryRegion.WRITABLE_ATTRIBUTE_NAME:
|
||||
case TargetMemoryRegion.EXECUTABLE_ATTRIBUTE_NAME:
|
||||
trace.updateViewsChangeRegionBlockFlags(this, lifespan);
|
||||
return;
|
||||
if (translator.keys.isRange(key)) {
|
||||
// NB. old/newValue are null here. The CREATED event just has the new entry.
|
||||
trace.updateViewsRefreshBlocks();
|
||||
}
|
||||
else if (translator.keys.isDisplay(key)) {
|
||||
trace.updateViewsChangeRegionBlockName(this);
|
||||
}
|
||||
else if (translator.keys.isFlag(key)) {
|
||||
trace.updateViewsChangeRegionBlockFlags(this, lifespan);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,11 +15,11 @@
|
|||
*/
|
||||
package ghidra.trace.database.module;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.dbg.target.schema.TargetObjectSchema;
|
||||
import ghidra.dbg.util.PathMatcher;
|
||||
import ghidra.dbg.util.PathPredicates.Align;
|
||||
import ghidra.dbg.util.PathUtils;
|
||||
|
@ -40,8 +40,19 @@ import ghidra.util.exception.DuplicateNameException;
|
|||
public class DBTraceObjectModule implements TraceObjectModule, DBTraceObjectInterface {
|
||||
|
||||
protected class ModuleChangeTranslator extends Translator<TraceModule> {
|
||||
private static final Map<TargetObjectSchema, Set<String>> KEYS_BY_SCHEMA =
|
||||
new WeakHashMap<>();
|
||||
|
||||
private final Set<String> keys;
|
||||
|
||||
protected ModuleChangeTranslator(DBTraceObject object, TraceModule iface) {
|
||||
super(TargetModule.RANGE_ATTRIBUTE_NAME, object, iface);
|
||||
TargetObjectSchema schema = object.getTargetSchema();
|
||||
synchronized (KEYS_BY_SCHEMA) {
|
||||
keys = KEYS_BY_SCHEMA.computeIfAbsent(schema, s -> Set.of(
|
||||
s.checkAliasedAttribute(TargetModule.RANGE_ATTRIBUTE_NAME),
|
||||
s.checkAliasedAttribute(TargetObject.DISPLAY_ATTRIBUTE_NAME)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -61,8 +72,7 @@ public class DBTraceObjectModule implements TraceObjectModule, DBTraceObjectInte
|
|||
|
||||
@Override
|
||||
protected boolean appliesToKey(String key) {
|
||||
return TargetModule.RANGE_ATTRIBUTE_NAME.equals(key) ||
|
||||
TargetObject.DISPLAY_ATTRIBUTE_NAME.equals(key);
|
||||
return keys.contains(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -15,7 +15,10 @@
|
|||
*/
|
||||
package ghidra.trace.database.module;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.dbg.target.schema.TargetObjectSchema;
|
||||
import ghidra.dbg.util.PathUtils;
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.trace.database.target.DBTraceObject;
|
||||
|
@ -34,8 +37,19 @@ import ghidra.util.LockHold;
|
|||
public class DBTraceObjectSection implements TraceObjectSection, DBTraceObjectInterface {
|
||||
|
||||
protected class SectionTranslator extends Translator<TraceSection> {
|
||||
private static final Map<TargetObjectSchema, Set<String>> KEYS_BY_SCHEMA =
|
||||
new WeakHashMap<>();
|
||||
|
||||
private final Set<String> keys;
|
||||
|
||||
protected SectionTranslator(DBTraceObject object, TraceSection iface) {
|
||||
super(TargetSection.RANGE_ATTRIBUTE_NAME, object, iface);
|
||||
TargetObjectSchema schema = object.getTargetSchema();
|
||||
synchronized (KEYS_BY_SCHEMA) {
|
||||
keys = KEYS_BY_SCHEMA.computeIfAbsent(schema, s -> Set.of(
|
||||
s.checkAliasedAttribute(TargetSection.RANGE_ATTRIBUTE_NAME),
|
||||
s.checkAliasedAttribute(TargetObject.DISPLAY_ATTRIBUTE_NAME)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -55,8 +69,7 @@ public class DBTraceObjectSection implements TraceObjectSection, DBTraceObjectIn
|
|||
|
||||
@Override
|
||||
protected boolean appliesToKey(String key) {
|
||||
return TargetSection.RANGE_ATTRIBUTE_NAME.equals(key) ||
|
||||
TargetObject.DISPLAY_ATTRIBUTE_NAME.equals(key);
|
||||
return keys.contains(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -15,9 +15,10 @@
|
|||
*/
|
||||
package ghidra.trace.database.stack;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.dbg.target.TargetStackFrame;
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.dbg.target.schema.TargetObjectSchema;
|
||||
import ghidra.dbg.util.PathUtils;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.CodeUnit;
|
||||
|
@ -37,13 +38,23 @@ import ghidra.trace.util.TraceChangeRecord;
|
|||
import ghidra.util.LockHold;
|
||||
|
||||
public class DBTraceObjectStackFrame implements TraceObjectStackFrame, DBTraceObjectInterface {
|
||||
private static final Map<TargetObjectSchema, Set<String>> KEYS_BY_SCHEMA = new WeakHashMap<>();
|
||||
|
||||
private final DBTraceObject object;
|
||||
private final Set<String> keys;
|
||||
|
||||
// TODO: Memorizing life is not optimal.
|
||||
// GP-1887 means to expose multiple lifespans in, e.g., TraceThread
|
||||
private LifeSet life = new DefaultLifeSet();
|
||||
|
||||
public DBTraceObjectStackFrame(DBTraceObject object) {
|
||||
this.object = object;
|
||||
|
||||
TargetObjectSchema schema = object.getTargetSchema();
|
||||
synchronized (KEYS_BY_SCHEMA) {
|
||||
keys = KEYS_BY_SCHEMA.computeIfAbsent(schema, s -> Set.of(
|
||||
schema.checkAliasedAttribute(TargetStackFrame.PC_ATTRIBUTE_NAME)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -136,7 +147,7 @@ public class DBTraceObjectStackFrame implements TraceObjectStackFrame, DBTraceOb
|
|||
TraceObjectChangeType.VALUE_CREATED.cast(rec);
|
||||
TraceObjectValue affected = cast.getAffectedObject();
|
||||
assert affected.getParent() == object;
|
||||
if (!TargetStackFrame.PC_ATTRIBUTE_NAME.equals(affected.getEntryKey())) {
|
||||
if (!keys.contains(affected.getEntryKey())) {
|
||||
return false;
|
||||
}
|
||||
if (object.getCanonicalParent(affected.getMaxSnap()) == null) {
|
||||
|
|
|
@ -137,6 +137,7 @@ public class DBTraceObject extends DBAnnotatedObject implements TraceObject {
|
|||
|
||||
protected final DBTraceObjectManager manager;
|
||||
|
||||
private TargetObjectSchema targetSchema;
|
||||
private Map<Class<? extends TraceObjectInterface>, TraceObjectInterface> ifaces;
|
||||
|
||||
private final Map<String, InternalTraceObjectValue> valueCache = new LinkedHashMap<>() {
|
||||
|
@ -461,29 +462,31 @@ public class DBTraceObject extends DBAnnotatedObject implements TraceObject {
|
|||
@Override
|
||||
public Collection<? extends InternalTraceObjectValue> getValues(Lifespan span, String key) {
|
||||
try (LockHold hold = manager.trace.lockRead()) {
|
||||
return doGetValues(span, key, true);
|
||||
String k = getTargetSchema().checkAliasedAttribute(key);
|
||||
return doGetValues(span, k, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InternalTraceObjectValue getValue(long snap, String key) {
|
||||
try (LockHold hold = manager.trace.lockRead()) {
|
||||
InternalTraceObjectValue cached = valueCache.get(key);
|
||||
String k = getTargetSchema().checkAliasedAttribute(key);
|
||||
InternalTraceObjectValue cached = valueCache.get(k);
|
||||
if (cached != null && !cached.isDeleted() && cached.getLifespan().contains(snap)) {
|
||||
return cached;
|
||||
}
|
||||
Long nullSnap = nullCache.get(key);
|
||||
Long nullSnap = nullCache.get(k);
|
||||
if (nullSnap != null && nullSnap.longValue() == snap) {
|
||||
return null;
|
||||
}
|
||||
InternalTraceObjectValue found = manager.valueMap
|
||||
.reduce(TraceObjectValueQuery.values(this, key, key, Lifespan.at(snap)))
|
||||
.reduce(TraceObjectValueQuery.values(this, k, k, Lifespan.at(snap)))
|
||||
.firstValue();
|
||||
if (found == null) {
|
||||
nullCache.put(key, snap);
|
||||
nullCache.put(k, snap);
|
||||
}
|
||||
else {
|
||||
valueCache.put(key, found);
|
||||
valueCache.put(k, found);
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
@ -493,7 +496,8 @@ public class DBTraceObject extends DBAnnotatedObject implements TraceObject {
|
|||
public Stream<? extends InternalTraceObjectValue> getOrderedValues(Lifespan span, String key,
|
||||
boolean forward) {
|
||||
try (LockHold hold = manager.trace.lockRead()) {
|
||||
return doGetValues(span, key, forward).stream();
|
||||
String k = getTargetSchema().checkAliasedAttribute(key);
|
||||
return doGetValues(span, k, forward).stream();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -599,11 +603,12 @@ public class DBTraceObject extends DBAnnotatedObject implements TraceObject {
|
|||
if (isDeleted()) {
|
||||
throw new IllegalStateException("Cannot set value on deleted object.");
|
||||
}
|
||||
String k = getTargetSchema().checkAliasedAttribute(key);
|
||||
if (resolution == ConflictResolution.DENY) {
|
||||
doCheckConflicts(lifespan, key, value);
|
||||
doCheckConflicts(lifespan, k, value);
|
||||
}
|
||||
else if (resolution == ConflictResolution.ADJUST) {
|
||||
lifespan = doAdjust(lifespan, key, value);
|
||||
lifespan = doAdjust(lifespan, k, value);
|
||||
}
|
||||
var setter = new ValueLifespanSetter(lifespan, value) {
|
||||
DBTraceObject canonicalLifeChanged = null;
|
||||
|
@ -612,7 +617,7 @@ public class DBTraceObject extends DBAnnotatedObject implements TraceObject {
|
|||
protected Iterable<InternalTraceObjectValue> getIntersecting(Long lower,
|
||||
Long upper) {
|
||||
return Collections.unmodifiableCollection(
|
||||
doGetValues(Lifespan.span(lower, upper), key, true));
|
||||
doGetValues(Lifespan.span(lower, upper), k, true));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -634,7 +639,7 @@ public class DBTraceObject extends DBAnnotatedObject implements TraceObject {
|
|||
|
||||
@Override
|
||||
protected InternalTraceObjectValue create(Lifespan range, Object value) {
|
||||
return doCreateValue(range, key, value);
|
||||
return doCreateValue(range, k, value);
|
||||
}
|
||||
};
|
||||
InternalTraceObjectValue result = setter.set(lifespan, value);
|
||||
|
@ -673,7 +678,11 @@ public class DBTraceObject extends DBAnnotatedObject implements TraceObject {
|
|||
|
||||
@Override
|
||||
public TargetObjectSchema getTargetSchema() {
|
||||
return manager.rootSchema.getSuccessorSchema(path.getKeyList());
|
||||
// NOTE: No need to synchronize. Schema is immutable.
|
||||
if (targetSchema == null) {
|
||||
targetSchema = manager.rootSchema.getSuccessorSchema(path.getKeyList());
|
||||
}
|
||||
return targetSchema;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package ghidra.trace.database.target;
|
||||
|
||||
import ghidra.dbg.target.schema.TargetObjectSchema;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.trace.database.space.DBTraceSpaceKey.DefaultDBTraceSpaceKey;
|
||||
|
@ -38,7 +39,13 @@ public interface DBTraceObjectInterface extends TraceObjectInterface, TraceUniqu
|
|||
private LifeSet life = new DefaultLifeSet();
|
||||
|
||||
public Translator(String spaceValueKey, DBTraceObject object, T iface) {
|
||||
this.spaceValueKey = spaceValueKey;
|
||||
if (spaceValueKey == null) {
|
||||
this.spaceValueKey = null;
|
||||
}
|
||||
else {
|
||||
TargetObjectSchema schema = object.getTargetSchema();
|
||||
this.spaceValueKey = schema.checkAliasedAttribute(spaceValueKey);
|
||||
}
|
||||
this.object = object;
|
||||
this.iface = iface;
|
||||
}
|
||||
|
|
|
@ -174,6 +174,8 @@ public class DBTraceObjectManager implements TraceObjectManager, DBTraceManager
|
|||
return size() > OBJECTS_CONTAINING_CACHE_SIZE;
|
||||
}
|
||||
};
|
||||
protected final Map<Class<? extends TraceObjectInterface>, Set<TargetObjectSchema>> //
|
||||
schemasByInterface = new HashMap<>();
|
||||
|
||||
public DBTraceObjectManager(DBHandle dbh, DBOpenMode openMode, ReadWriteLock lock,
|
||||
TaskMonitor monitor, Language baseLanguage, DBTrace trace)
|
||||
|
@ -221,6 +223,9 @@ public class DBTraceObjectManager implements TraceObjectManager, DBTraceManager
|
|||
valueTree.invalidateCache();
|
||||
schemaStore.invalidateCache();
|
||||
loadRootSchema();
|
||||
objectsContainingCache.clear();
|
||||
// Though rare, the root schema could change
|
||||
schemasByInterface.clear();
|
||||
}
|
||||
|
||||
@Internal
|
||||
|
@ -449,6 +454,8 @@ public class DBTraceObjectManager implements TraceObjectManager, DBTraceManager
|
|||
objectStore.deleteAll();
|
||||
schemaStore.deleteAll();
|
||||
rootSchema = null;
|
||||
objectsContainingCache.clear();
|
||||
schemasByInterface.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -515,13 +522,6 @@ public class DBTraceObjectManager implements TraceObjectManager, DBTraceManager
|
|||
}
|
||||
}
|
||||
|
||||
protected <I extends TraceObjectInterface> Stream<I> doParentsHaving(
|
||||
Stream<? extends TraceObjectValue> values, Class<I> iface) {
|
||||
return values.map(v -> v.getParent())
|
||||
.map(o -> o.queryInterface(iface))
|
||||
.filter(i -> i != null);
|
||||
}
|
||||
|
||||
protected void invalidateObjectsContainingCache() {
|
||||
synchronized (objectsContainingCache) {
|
||||
objectsContainingCache.clear();
|
||||
|
@ -530,8 +530,8 @@ public class DBTraceObjectManager implements TraceObjectManager, DBTraceManager
|
|||
|
||||
protected Collection<? extends TraceObjectInterface> doGetObjectsContaining(
|
||||
ObjectsContainingKey key) {
|
||||
return doParentsHaving(getValuesAt(key.snap, key.address, key.key).stream(), key.iface)
|
||||
.collect(Collectors.toSet());
|
||||
return getObjectsIntersecting(Lifespan.at(key.snap),
|
||||
new AddressRangeImpl(key.address, key.address), key.key, key.iface);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
@ -555,11 +555,36 @@ public class DBTraceObjectManager implements TraceObjectManager, DBTraceManager
|
|||
return col.iterator().next();
|
||||
}
|
||||
|
||||
protected Set<TargetObjectSchema> collectSchemasForInterface(
|
||||
Class<? extends TraceObjectInterface> iface) {
|
||||
if (rootSchema == null) {
|
||||
return Set.of();
|
||||
}
|
||||
Class<? extends TargetObject> targetIf = TraceObjectInterfaceUtils.toTargetIf(iface);
|
||||
Set<TargetObjectSchema> result = new HashSet<>();
|
||||
for (TargetObjectSchema schema : rootSchema.getContext().getAllSchemas()) {
|
||||
if (schema.getInterfaces().contains(targetIf)) {
|
||||
result.add(schema);
|
||||
}
|
||||
}
|
||||
return Set.copyOf(result);
|
||||
}
|
||||
|
||||
public <I extends TraceObjectInterface> Collection<I> getObjectsIntersecting(
|
||||
Lifespan lifespan, AddressRange range, String key, Class<I> iface) {
|
||||
try (LockHold hold = trace.lockRead()) {
|
||||
return doParentsHaving(getValuesIntersecting(lifespan, range, key).stream(), iface)
|
||||
.collect(Collectors.toSet());
|
||||
Set<TargetObjectSchema> schemas;
|
||||
synchronized (schemasByInterface) {
|
||||
schemas =
|
||||
schemasByInterface.computeIfAbsent(iface, this::collectSchemasForInterface);
|
||||
}
|
||||
Map<String, List<TargetObjectSchema>> schemasByAliasTo =
|
||||
schemas.stream().collect(Collectors.groupingBy(s -> s.checkAliasedAttribute(key)));
|
||||
return schemasByAliasTo.entrySet().stream().flatMap(ent -> {
|
||||
return getValuesIntersecting(lifespan, range, ent.getKey()).stream()
|
||||
.map(v -> v.getParent())
|
||||
.filter(o -> ent.getValue().contains(o.getTargetSchema()));
|
||||
}).map(o -> o.queryInterface(iface)).collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -573,7 +598,7 @@ public class DBTraceObjectManager implements TraceObjectManager, DBTraceManager
|
|||
public <I extends TraceObjectInterface> AddressSetView getObjectsAddressSet(long snap,
|
||||
String key, Class<I> ifaceCls, Predicate<? super I> predicate) {
|
||||
return valueMap.getAddressSetView(Lifespan.at(snap), v -> {
|
||||
if (!key.equals(v.getEntryKey())) {
|
||||
if (!v.hasEntryKey(key)) {
|
||||
return false;
|
||||
}
|
||||
TraceObject parent = v.getParent();
|
||||
|
|
|
@ -15,7 +15,10 @@
|
|||
*/
|
||||
package ghidra.trace.database.thread;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.dbg.target.schema.TargetObjectSchema;
|
||||
import ghidra.trace.database.target.DBTraceObject;
|
||||
import ghidra.trace.database.target.DBTraceObjectInterface;
|
||||
import ghidra.trace.model.Lifespan;
|
||||
|
@ -32,8 +35,19 @@ import ghidra.util.exception.DuplicateNameException;
|
|||
public class DBTraceObjectThread implements TraceObjectThread, DBTraceObjectInterface {
|
||||
|
||||
protected class ThreadChangeTranslator extends Translator<TraceThread> {
|
||||
private static final Map<TargetObjectSchema, Set<String>> KEYS_BY_SCHEMA =
|
||||
new WeakHashMap<>();
|
||||
|
||||
private final Set<String> keys;
|
||||
|
||||
protected ThreadChangeTranslator(DBTraceObject object, TraceThread iface) {
|
||||
super(null, object, iface);
|
||||
TargetObjectSchema schema = object.getTargetSchema();
|
||||
synchronized (KEYS_BY_SCHEMA) {
|
||||
keys = KEYS_BY_SCHEMA.computeIfAbsent(schema, s -> Set.of(
|
||||
s.checkAliasedAttribute(KEY_COMMENT),
|
||||
s.checkAliasedAttribute(TargetObject.DISPLAY_ATTRIBUTE_NAME)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -53,8 +67,7 @@ public class DBTraceObjectThread implements TraceObjectThread, DBTraceObjectInte
|
|||
|
||||
@Override
|
||||
protected boolean appliesToKey(String key) {
|
||||
return KEY_COMMENT.equals(key) ||
|
||||
TargetObject.DISPLAY_ATTRIBUTE_NAME.equals(key);
|
||||
return keys.contains(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -86,7 +86,7 @@ public interface TraceObject extends TraceUniqueObject {
|
|||
* contain the given lifespan. Only the canonical path is considered when looking for existing
|
||||
* ancestry.
|
||||
*
|
||||
* @param the minimum lifespan of edges from the root to this object
|
||||
* @param lifespan the minimum lifespan of edges from the root to this object
|
||||
* @param resolution the rule for handling duplicate keys when setting values.
|
||||
* @return the value path from root to the newly inserted object
|
||||
*/
|
||||
|
@ -155,6 +155,9 @@ public interface TraceObject extends TraceUniqueObject {
|
|||
/**
|
||||
* Get all paths actually leading to this object, from the root, within the given span
|
||||
*
|
||||
* <p>
|
||||
* Aliased keys are excluded.
|
||||
*
|
||||
* @param span the span which every value entry on each path must intersect
|
||||
* @return the paths
|
||||
*/
|
||||
|
@ -204,6 +207,9 @@ public interface TraceObject extends TraceUniqueObject {
|
|||
/**
|
||||
* Get all values intersecting the given span and whose child is this object
|
||||
*
|
||||
* <p>
|
||||
* Aliased keys are excluded.
|
||||
*
|
||||
* @param span the span
|
||||
* @return the parent values
|
||||
*/
|
||||
|
@ -212,6 +218,10 @@ public interface TraceObject extends TraceUniqueObject {
|
|||
/**
|
||||
* Get all values (elements and attributes) of this object intersecting the given span
|
||||
*
|
||||
* <p>
|
||||
* Aliased keys are excluded.
|
||||
*
|
||||
* @param span the span
|
||||
* @return the values
|
||||
*/
|
||||
Collection<? extends TraceObjectValue> getValues(Lifespan span);
|
||||
|
@ -219,6 +229,9 @@ public interface TraceObject extends TraceUniqueObject {
|
|||
/**
|
||||
* Get values with the given key intersecting the given span
|
||||
*
|
||||
* <p>
|
||||
* If the key is an alias, the target key's values are retrieved instead.
|
||||
*
|
||||
* @param span the span
|
||||
* @param key the key
|
||||
* @return the collection of values
|
||||
|
@ -228,6 +241,9 @@ public interface TraceObject extends TraceUniqueObject {
|
|||
/**
|
||||
* Get values with the given key intersecting the given span ordered by time
|
||||
*
|
||||
* <p>
|
||||
* If the key is an alias, the target key's values are retrieved instead.
|
||||
*
|
||||
* @param span the span
|
||||
* @param key the key
|
||||
* @param forward true to order from least- to most-recent, false for most- to least-recent
|
||||
|
@ -239,6 +255,7 @@ public interface TraceObject extends TraceUniqueObject {
|
|||
/**
|
||||
* Get all elements of this object intersecting the given span
|
||||
*
|
||||
* @param span the span
|
||||
* @return the element values
|
||||
*/
|
||||
Collection<? extends TraceObjectValue> getElements(Lifespan span);
|
||||
|
@ -246,6 +263,10 @@ public interface TraceObject extends TraceUniqueObject {
|
|||
/**
|
||||
* Get all attributes of this object intersecting the given span
|
||||
*
|
||||
* <p>
|
||||
* Aliased keys are excluded.
|
||||
*
|
||||
* @param span the span
|
||||
* @return the attribute values
|
||||
*/
|
||||
Collection<? extends TraceObjectValue> getAttributes(Lifespan span);
|
||||
|
@ -253,6 +274,9 @@ public interface TraceObject extends TraceUniqueObject {
|
|||
/**
|
||||
* Get the value for the given snap and key
|
||||
*
|
||||
* <p>
|
||||
* If the key is an alias, the target key's value is retrieved instead.
|
||||
*
|
||||
* @param snap the snap
|
||||
* @param key the key
|
||||
* @return the value entry
|
||||
|
@ -302,6 +326,10 @@ public interface TraceObject extends TraceUniqueObject {
|
|||
* Stream all ancestor values of this object matching the given predicates, intersecting the
|
||||
* given span
|
||||
*
|
||||
* <p>
|
||||
* Aliased keys are excluded. The predicates should be formulated to use the aliases' target
|
||||
* attributes.
|
||||
*
|
||||
* @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
|
||||
|
@ -313,6 +341,10 @@ public interface TraceObject extends TraceUniqueObject {
|
|||
* Stream all ancestor values of this object matching the given predicates, intersecting the
|
||||
* given span
|
||||
*
|
||||
* <p>
|
||||
* Aliased keys are excluded. The predicates should be formulated to use the aliases' target
|
||||
* attributes.
|
||||
*
|
||||
* @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
|
||||
|
@ -324,6 +356,10 @@ public interface TraceObject extends TraceUniqueObject {
|
|||
* Stream all successor values of this object matching the given predicates, intersecting the
|
||||
* given span
|
||||
*
|
||||
* <p>
|
||||
* Aliased keys are excluded. The predicates should be formulated to use the aliases' target
|
||||
* attributes.
|
||||
*
|
||||
* @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
|
||||
|
@ -335,6 +371,10 @@ public interface TraceObject extends TraceUniqueObject {
|
|||
* Stream all successor values of this object at the given relative path, intersecting the given
|
||||
* span, ordered by time.
|
||||
*
|
||||
* <p>
|
||||
* Aliased keys are excluded. The predicates should be formulated to use the aliases' target
|
||||
* attributes.
|
||||
*
|
||||
* @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
|
||||
|
@ -348,9 +388,11 @@ public interface TraceObject extends TraceUniqueObject {
|
|||
*
|
||||
* <p>
|
||||
* If an object has a disjoint life, i.e., multiple canonical parents, then only the
|
||||
* least-recent of those is traversed.
|
||||
* least-recent of those is traversed. Aliased keys are excluded; those can't be canonical
|
||||
* anyway.
|
||||
*
|
||||
* @param relativePath the path relative to this object
|
||||
* @param relativePredicates predicates on the relative path from this object to desired
|
||||
* successors
|
||||
* @return the stream of value paths
|
||||
*/
|
||||
Stream<? extends TraceObjectValPath> getCanonicalSuccessors(PathPredicates relativePredicates);
|
||||
|
@ -358,6 +400,9 @@ public interface TraceObject extends TraceUniqueObject {
|
|||
/**
|
||||
* Set a value for the given lifespan
|
||||
*
|
||||
* <p>
|
||||
* If the key is an alias, the target key's value is set instead.
|
||||
*
|
||||
* @param lifespan the lifespan of the value
|
||||
* @param key the key to set
|
||||
* @param value the new value
|
||||
|
@ -374,7 +419,8 @@ public interface TraceObject extends TraceUniqueObject {
|
|||
* <p>
|
||||
* Setting a value of {@code null} effectively deletes the value for the given lifespan and
|
||||
* returns {@code null}. Values of the same key intersecting the given lifespan or either
|
||||
* truncated or deleted.
|
||||
* truncated or deleted. If the key is an alias, the target key's value is set instead.
|
||||
*
|
||||
*
|
||||
* @param lifespan the lifespan of the value
|
||||
* @param key the key to set
|
||||
|
@ -453,7 +499,7 @@ public interface TraceObject extends TraceUniqueObject {
|
|||
* Search for ancestors on the canonical path having the given target interface
|
||||
*
|
||||
* <p>
|
||||
* The object may not yet be inserted at its canonical path
|
||||
* The object may not yet be inserted at its canonical path.
|
||||
*
|
||||
* @param targetIf the interface class
|
||||
* @return the stream of objects
|
||||
|
@ -465,7 +511,7 @@ public interface TraceObject extends TraceUniqueObject {
|
|||
* Search for ancestors on the canonical path providing the given interface
|
||||
*
|
||||
* <p>
|
||||
* The object may not yet be inserted at its canonical path
|
||||
* The object may not yet be inserted at its canonical path.
|
||||
*
|
||||
* @param <I> the interface type
|
||||
* @param ifClass the interface class
|
||||
|
|
|
@ -43,6 +43,20 @@ public interface TraceObjectValue {
|
|||
*/
|
||||
String getEntryKey();
|
||||
|
||||
/**
|
||||
* Check if the given key (or alias) matches this entry's key
|
||||
*
|
||||
* @param keyOrAlias the key or alias
|
||||
* @return true if the key matches this entry's key, or it is an alias for it
|
||||
*/
|
||||
default boolean hasEntryKey(String keyOrAlias) {
|
||||
TraceObject parent = getParent();
|
||||
if (parent == null) {
|
||||
return getEntryKey().equals(keyOrAlias);
|
||||
}
|
||||
return getEntryKey().equals(parent.getTargetSchema().checkAliasedAttribute(keyOrAlias));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the "canonical path" of this value
|
||||
*
|
||||
|
@ -50,7 +64,7 @@ public interface TraceObjectValue {
|
|||
* This is the parent's canonical path extended by this value's entry key. Note, in the case
|
||||
* this value has a child object, this is not necessarily its canonical path.
|
||||
*
|
||||
* @return
|
||||
* @return the canonical path
|
||||
*/
|
||||
TraceObjectKeyPath getCanonicalPath();
|
||||
|
||||
|
@ -127,7 +141,7 @@ public interface TraceObjectValue {
|
|||
* uniquely determined at a given snap. Thus, when lifespans are being adjusted, such conflicts
|
||||
* must be resolved.
|
||||
*
|
||||
* @param lifespan the new lifespan
|
||||
* @param span the new lifespan
|
||||
* @param resolution specifies how to resolve duplicate keys with intersecting lifespans
|
||||
* @throws DuplicateKeyException if there are denied duplicate keys
|
||||
*/
|
||||
|
|
|
@ -88,6 +88,7 @@ public class DBTraceObjectManagerTest extends AbstractGhidraHeadlessIntegrationT
|
|||
</schema>
|
||||
<schema name='Region' elementResync='NEVER' attributeResync='NEVER'>
|
||||
<interface name='MemoryRegion' />
|
||||
<attribute-alias from="_range" to="Range" />
|
||||
</schema>
|
||||
</context>
|
||||
""";
|
||||
|
@ -1175,4 +1176,30 @@ public class DBTraceObjectManagerTest extends AbstractGhidraHeadlessIntegrationT
|
|||
|
||||
b.trace.getObjectManager().getValuesIntersecting(Lifespan.ALL, b.range(0, -1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAttributeAliasing() {
|
||||
TraceObject regionText;
|
||||
try (Transaction tx = b.startTransaction()) {
|
||||
TraceObjectValue rootVal =
|
||||
manager.createRootObject(ctx.getSchema(new SchemaName("Session")));
|
||||
root = rootVal.getChild();
|
||||
|
||||
regionText =
|
||||
manager.createObject(TraceObjectKeyPath.parse("Targets[0].Memory[bin:.text]"));
|
||||
regionText.insert(Lifespan.nowOn(0), ConflictResolution.DENY);
|
||||
regionText.setAttribute(Lifespan.nowOn(0), "_range", b.range(0x00400000, 0x00401000));
|
||||
regionText.setAttribute(Lifespan.nowOn(0), "Range", b.range(0x00400000, 0x00402000));
|
||||
}
|
||||
|
||||
assertEquals(ctx.getSchema(new SchemaName("Region")), regionText.getTargetSchema());
|
||||
assertEquals(Set.of(
|
||||
regionText.getAttribute(0, "Range")),
|
||||
Set.copyOf(regionText.getAttributes(Lifespan.ALL)));
|
||||
assertEquals(b.range(0x00400000, 0x00402000),
|
||||
regionText.getAttribute(0, "_range").getValue());
|
||||
assertEquals(b.range(0x00400000, 0x00402000),
|
||||
regionText.getAttribute(0, "Range").getValue());
|
||||
assertEquals("Range", regionText.getAttribute(0, "_range").getEntryKey());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue