mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
GP-5213: Fix editing registers presented as primitives.
This commit is contained in:
parent
18ef5ceea3
commit
e0ccc4883b
26 changed files with 384 additions and 287 deletions
|
@ -28,7 +28,7 @@ import ghidra.trace.model.breakpoint.*;
|
|||
import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.trace.model.target.iface.TraceObjectTogglable;
|
||||
import ghidra.trace.model.target.info.TraceObjectInterfaceUtils;
|
||||
import ghidra.trace.model.target.path.PathMatcher;
|
||||
import ghidra.trace.model.target.path.PathFilter;
|
||||
import ghidra.trace.model.target.schema.TraceObjectSchema;
|
||||
import ghidra.trace.model.thread.*;
|
||||
import ghidra.trace.util.*;
|
||||
|
@ -301,8 +301,8 @@ public class DBTraceObjectBreakpointLocation
|
|||
return threads;
|
||||
}
|
||||
|
||||
PathMatcher procMatcher = schema.searchFor(TraceObjectProcess.class, false);
|
||||
return object.getAncestorsRoot(getLifespan(), procMatcher)
|
||||
PathFilter procFilter = schema.searchFor(TraceObjectProcess.class, false);
|
||||
return object.getAncestorsRoot(getLifespan(), procFilter)
|
||||
.flatMap(proc -> proc.getSource(object)
|
||||
.querySuccessorsInterface(getLifespan(),
|
||||
TraceObjectThread.class, true))
|
||||
|
|
|
@ -15,12 +15,10 @@
|
|||
*/
|
||||
package ghidra.trace.database.guest;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import ghidra.pcode.utils.Utils;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.lang.Register;
|
||||
|
@ -31,8 +29,8 @@ import ghidra.trace.model.memory.*;
|
|||
import ghidra.trace.model.symbol.*;
|
||||
import ghidra.trace.model.target.*;
|
||||
import ghidra.trace.model.target.path.KeyPath;
|
||||
import ghidra.trace.model.target.path.PathFilter;
|
||||
import ghidra.trace.model.target.path.PathFilter.Align;
|
||||
import ghidra.trace.model.target.path.PathMatcher;
|
||||
import ghidra.trace.model.target.schema.TraceObjectSchema;
|
||||
import ghidra.trace.util.TraceChangeRecord;
|
||||
import ghidra.trace.util.TraceEvents;
|
||||
|
@ -51,108 +49,6 @@ public enum DBTraceObjectRegisterSupport {
|
|||
}
|
||||
};
|
||||
|
||||
static class RegisterValueException extends Exception {
|
||||
public RegisterValueException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
static class LazyValues {
|
||||
private final TraceObjectValue registerValue;
|
||||
private BigInteger value;
|
||||
private int bitLength = -1;
|
||||
private byte[] be;
|
||||
private byte[] le;
|
||||
|
||||
public LazyValues(TraceObjectValue registerValue) {
|
||||
this.registerValue = registerValue;
|
||||
}
|
||||
|
||||
BigInteger convertRegisterValueToBigInteger() throws RegisterValueException {
|
||||
Object val = registerValue.getValue();
|
||||
if (val instanceof String s) {
|
||||
try {
|
||||
return new BigInteger(s, 16);
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
throw new RegisterValueException(
|
||||
"Invalid register value " + s + ". Must be hex digits only.");
|
||||
}
|
||||
}
|
||||
else if (val instanceof byte[] arr) {
|
||||
// NOTE: Reg object values are always big endian
|
||||
return new BigInteger(1, arr);
|
||||
}
|
||||
else if (val instanceof Byte b) {
|
||||
return BigInteger.valueOf(b);
|
||||
}
|
||||
else if (val instanceof Short s) {
|
||||
return BigInteger.valueOf(s);
|
||||
}
|
||||
else if (val instanceof Integer i) {
|
||||
return BigInteger.valueOf(i);
|
||||
}
|
||||
else if (val instanceof Long l) {
|
||||
return BigInteger.valueOf(l);
|
||||
}
|
||||
else if (val instanceof Address a) {
|
||||
return a.getOffsetAsBigInteger();
|
||||
}
|
||||
throw new RegisterValueException(
|
||||
"Cannot convert register value: (" + registerValue.getValue().getClass() + ") '" +
|
||||
registerValue.getValue() +
|
||||
"'");
|
||||
}
|
||||
|
||||
int getRegisterValueBitLength() throws RegisterValueException {
|
||||
Object objBitLength = registerValue.getParent()
|
||||
.getValue(registerValue.getMinSnap(), TraceObjectRegister.KEY_BITLENGTH)
|
||||
.getValue();
|
||||
if (!(objBitLength instanceof Number)) {
|
||||
throw new RegisterValueException(
|
||||
"Register length is not numeric: (" + objBitLength.getClass() + ") '" +
|
||||
objBitLength + "'");
|
||||
}
|
||||
return ((Number) objBitLength).intValue();
|
||||
}
|
||||
|
||||
BigInteger getValue() throws RegisterValueException {
|
||||
if (value != null) {
|
||||
return value;
|
||||
}
|
||||
return value = convertRegisterValueToBigInteger();
|
||||
}
|
||||
|
||||
int getBitLength() throws RegisterValueException {
|
||||
if (bitLength != -1) {
|
||||
return bitLength;
|
||||
}
|
||||
return bitLength = getRegisterValueBitLength();
|
||||
}
|
||||
|
||||
int getByteLength() throws RegisterValueException {
|
||||
return (getBitLength() + 7) / 8;
|
||||
}
|
||||
|
||||
byte[] getBytesBigEndian() throws RegisterValueException {
|
||||
if (be != null) {
|
||||
return be;
|
||||
}
|
||||
return be = Utils.bigIntegerToBytes(getValue(), getByteLength(), true);
|
||||
}
|
||||
|
||||
byte[] getBytesLittleEndian() throws RegisterValueException {
|
||||
if (le != null) {
|
||||
return le;
|
||||
}
|
||||
return le = Utils.bigIntegerToBytes(getValue(), getByteLength(), false);
|
||||
}
|
||||
|
||||
public byte[] getBytes(boolean isBigEndian) throws RegisterValueException {
|
||||
return isBigEndian ? getBytesBigEndian() : getBytesLittleEndian();
|
||||
}
|
||||
}
|
||||
|
||||
protected AddressSpace findRegisterOverlay(TraceObject object) {
|
||||
TraceObject container = object
|
||||
.findCanonicalAncestorsInterface(TraceObjectRegisterContainer.class)
|
||||
|
@ -173,7 +69,8 @@ public enum DBTraceObjectRegisterSupport {
|
|||
}
|
||||
|
||||
protected void onValueCreatedTransferToPlatformRegister(TraceObjectValue registerValue,
|
||||
TracePlatform platform, String name, LazyValues lazy) throws RegisterValueException {
|
||||
TracePlatform platform, String name, RegisterValueConverter rvc)
|
||||
throws RegisterValueException {
|
||||
Register register = platform.getLanguage().getRegister(name);
|
||||
if (register == null) {
|
||||
return;
|
||||
|
@ -188,7 +85,7 @@ public enum DBTraceObjectRegisterSupport {
|
|||
long minSnap = registerValue.getMinSnap();
|
||||
if (hostSpace.isMemorySpace()) {
|
||||
mem.getMemorySpace(hostSpace, true)
|
||||
.setValue(platform, minSnap, new RegisterValue(register, lazy.getValue()));
|
||||
.setValue(platform, minSnap, new RegisterValue(register, rvc.getValue()));
|
||||
}
|
||||
else if (hostSpace.isRegisterSpace()) {
|
||||
AddressSpace overlay = findRegisterOverlay(registerValue);
|
||||
|
@ -196,7 +93,7 @@ public enum DBTraceObjectRegisterSupport {
|
|||
return;
|
||||
}
|
||||
mem.getMemorySpace(overlay, true)
|
||||
.setValue(platform, minSnap, new RegisterValue(register, lazy.getValue()));
|
||||
.setValue(platform, minSnap, new RegisterValue(register, rvc.getValue()));
|
||||
}
|
||||
else {
|
||||
throw new AssertionError();
|
||||
|
@ -205,10 +102,10 @@ public enum DBTraceObjectRegisterSupport {
|
|||
|
||||
protected void transferValueToPlatformRegister(TraceObjectValue registerValue,
|
||||
TracePlatform platform, TraceMemorySpace mem, Register register) {
|
||||
LazyValues lazy = new LazyValues(registerValue);
|
||||
RegisterValueConverter rvc = new RegisterValueConverter(registerValue);
|
||||
try {
|
||||
mem.setValue(platform, registerValue.getMinSnap(),
|
||||
new RegisterValue(register, lazy.getValue()));
|
||||
new RegisterValue(register, rvc.getValue()));
|
||||
}
|
||||
catch (RegisterValueException e) {
|
||||
Msg.error(this, e.getMessage());
|
||||
|
@ -278,10 +175,10 @@ public enum DBTraceObjectRegisterSupport {
|
|||
Address address = mem.getAddressSpace().getOverlayAddress(label.getAddress());
|
||||
for (TraceObjectValue registerValue : it(registerObject.getOrderedValues(
|
||||
label.getLifespan(), TraceObjectRegister.KEY_VALUE, true))) {
|
||||
LazyValues lazy = new LazyValues(registerValue);
|
||||
RegisterValueConverter rvc = new RegisterValueConverter(registerValue);
|
||||
try {
|
||||
long minSnap = registerValue.getMinSnap();
|
||||
mem.putBytes(minSnap, address, ByteBuffer.wrap(lazy.getBytes(isBigEndian)));
|
||||
mem.putBytes(minSnap, address, ByteBuffer.wrap(rvc.getBytes(isBigEndian)));
|
||||
}
|
||||
catch (RegisterValueException e) {
|
||||
Msg.error(this, e.getMessage());
|
||||
|
@ -293,15 +190,15 @@ public enum DBTraceObjectRegisterSupport {
|
|||
throws RegisterValueException {
|
||||
TraceObject registerObject = registerValue.getParent();
|
||||
Trace trace = registerValue.getTrace();
|
||||
LazyValues lazy = new LazyValues(registerValue);
|
||||
RegisterValueConverter rvc = new RegisterValueConverter(registerValue);
|
||||
|
||||
String name = getRegisterName(registerObject);
|
||||
|
||||
TracePlatformManager platformManager = trace.getPlatformManager();
|
||||
onValueCreatedTransferToPlatformRegister(registerValue, platformManager.getHostPlatform(),
|
||||
name, lazy);
|
||||
name, rvc);
|
||||
for (TracePlatform platform : platformManager.getGuestPlatforms()) {
|
||||
onValueCreatedTransferToPlatformRegister(registerValue, platform, name, lazy);
|
||||
onValueCreatedTransferToPlatformRegister(registerValue, platform, name, rvc);
|
||||
}
|
||||
|
||||
TraceNamespaceSymbolView namespaces = trace.getSymbolManager().namespaces();
|
||||
|
@ -311,7 +208,7 @@ public enum DBTraceObjectRegisterSupport {
|
|||
for (TraceLabelSymbol label : trace.getSymbolManager()
|
||||
.labels()
|
||||
.getChildrenNamed(name, nsRegMapBE)) {
|
||||
transferRegisterValueToLabel(registerValue, label, lazy.getBytesBigEndian());
|
||||
transferRegisterValueToLabel(registerValue, label, rvc.getBytesBigEndian());
|
||||
}
|
||||
}
|
||||
TraceNamespaceSymbol nsRegMapLE =
|
||||
|
@ -320,7 +217,7 @@ public enum DBTraceObjectRegisterSupport {
|
|||
for (TraceLabelSymbol label : trace.getSymbolManager()
|
||||
.labels()
|
||||
.getChildrenNamed(name, nsRegMapLE)) {
|
||||
transferRegisterValueToLabel(registerValue, label, lazy.getBytesLittleEndian());
|
||||
transferRegisterValueToLabel(registerValue, label, rvc.getBytesLittleEndian());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -349,10 +246,10 @@ public enum DBTraceObjectRegisterSupport {
|
|||
if (schema == null) {
|
||||
return;
|
||||
}
|
||||
PathMatcher matcher = schema.searchFor(TraceObjectRegister.class, true);
|
||||
matcher = matcher.applyKeys(Align.RIGHT, List.of(label.getName()));
|
||||
PathFilter filter = schema.searchFor(TraceObjectRegister.class, true);
|
||||
PathFilter applied = filter.applyKeys(Align.RIGHT, List.of(label.getName()));
|
||||
for (TraceObjectValPath path : it(
|
||||
objectManager.getValuePaths(label.getLifespan(), matcher))) {
|
||||
objectManager.getValuePaths(label.getLifespan(), applied))) {
|
||||
Object regRaw = path.getDestinationValue(objectManager.getRootObject());
|
||||
if (regRaw instanceof TraceObject regObj) {
|
||||
transferRegisterObjectToLabel(regObj, label, isBigEndian);
|
||||
|
|
|
@ -26,9 +26,8 @@ import ghidra.trace.model.guest.TracePlatform;
|
|||
import ghidra.trace.model.memory.TraceObjectRegister;
|
||||
import ghidra.trace.model.symbol.*;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.trace.model.target.path.KeyPath;
|
||||
import ghidra.trace.model.target.path.*;
|
||||
import ghidra.trace.model.target.path.PathFilter.Align;
|
||||
import ghidra.trace.model.target.path.PathMatcher;
|
||||
import ghidra.trace.model.target.schema.TraceObjectSchema;
|
||||
import ghidra.trace.util.TraceRegisterUtils;
|
||||
import ghidra.util.LockHold;
|
||||
|
@ -112,34 +111,31 @@ public interface InternalTracePlatform extends TracePlatform {
|
|||
}
|
||||
|
||||
@Override
|
||||
default PathMatcher getConventionalRegisterPath(TraceObjectSchema schema, KeyPath path,
|
||||
default PathFilter getConventionalRegisterPath(TraceObjectSchema schema, KeyPath path,
|
||||
Collection<String> names) {
|
||||
PathMatcher matcher = schema.searchFor(TraceObjectRegister.class, path, true);
|
||||
if (matcher.isNone()) {
|
||||
return matcher;
|
||||
PathFilter filter = schema.searchFor(TraceObjectRegister.class, path, true);
|
||||
if (filter.isNone()) {
|
||||
return PathFilter.NONE;
|
||||
}
|
||||
PathMatcher result = new PathMatcher();
|
||||
for (String name : names) {
|
||||
result.addAll(matcher.applyKeys(Align.RIGHT, List.of(name)));
|
||||
}
|
||||
return result;
|
||||
return PathMatcher.any(names.stream()
|
||||
.flatMap(n -> filter.applyKeys(Align.RIGHT, List.of(n)).getPatterns().stream()));
|
||||
}
|
||||
|
||||
@Override
|
||||
default PathMatcher getConventionalRegisterPath(TraceObjectSchema schema, KeyPath path,
|
||||
default PathFilter getConventionalRegisterPath(TraceObjectSchema schema, KeyPath path,
|
||||
Register register) {
|
||||
return getConventionalRegisterPath(schema, path,
|
||||
getConventionalRegisterObjectNames(register));
|
||||
}
|
||||
|
||||
@Override
|
||||
default PathMatcher getConventionalRegisterPath(TraceObject container, Register register) {
|
||||
default PathFilter getConventionalRegisterPath(TraceObject container, Register register) {
|
||||
return getConventionalRegisterPath(container.getSchema(),
|
||||
container.getCanonicalPath(), register);
|
||||
}
|
||||
|
||||
@Override
|
||||
default PathMatcher getConventionalRegisterPath(AddressSpace space, Register register) {
|
||||
default PathFilter getConventionalRegisterPath(AddressSpace space, Register register) {
|
||||
KeyPath path = KeyPath.parse(space.getName());
|
||||
TraceObjectSchema rootSchema = getTrace().getObjectManager().getRootSchema();
|
||||
if (rootSchema == null) {
|
||||
|
|
|
@ -28,8 +28,8 @@ import ghidra.trace.model.target.TraceObject;
|
|||
import ghidra.trace.model.target.iface.TraceObjectInterface;
|
||||
import ghidra.trace.model.target.info.TraceObjectInterfaceUtils;
|
||||
import ghidra.trace.model.target.path.KeyPath;
|
||||
import ghidra.trace.model.target.path.PathFilter;
|
||||
import ghidra.trace.model.target.path.PathFilter.Align;
|
||||
import ghidra.trace.model.target.path.PathMatcher;
|
||||
import ghidra.trace.model.target.schema.TraceObjectSchema;
|
||||
import ghidra.trace.util.*;
|
||||
import ghidra.util.LockHold;
|
||||
|
@ -255,8 +255,8 @@ public class DBTraceObjectModule implements TraceObjectModule, DBTraceObjectInte
|
|||
|
||||
@Override
|
||||
public TraceObjectSection getSectionByName(String sectionName) {
|
||||
PathMatcher matcher = object.getSchema().searchFor(TraceObjectSection.class, true);
|
||||
PathMatcher applied = matcher.applyKeys(Align.LEFT, List.of(sectionName));
|
||||
PathFilter filter = object.getSchema().searchFor(TraceObjectSection.class, true);
|
||||
PathFilter applied = filter.applyKeys(Align.LEFT, List.of(sectionName));
|
||||
return object.getSuccessors(getLifespan(), applied)
|
||||
.map(p -> p.getDestination(object).queryInterface(TraceObjectSection.class))
|
||||
.findAny()
|
||||
|
|
|
@ -24,7 +24,8 @@ import ghidra.trace.database.target.DBTraceObjectInterface;
|
|||
import ghidra.trace.model.Lifespan;
|
||||
import ghidra.trace.model.stack.*;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.trace.model.target.path.*;
|
||||
import ghidra.trace.model.target.path.KeyPath;
|
||||
import ghidra.trace.model.target.path.PathFilter;
|
||||
import ghidra.trace.model.target.schema.TraceObjectSchema;
|
||||
import ghidra.trace.model.thread.TraceObjectThread;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
|
@ -100,9 +101,9 @@ public class DBTraceObjectStack implements TraceObjectStack, DBTraceObjectInterf
|
|||
|
||||
protected TraceObjectStackFrame doAddStackFrame(int level) {
|
||||
try (LockHold hold = object.getTrace().lockWrite()) {
|
||||
PathMatcher matcher =
|
||||
PathFilter filter =
|
||||
object.getSchema().searchFor(TraceObjectStackFrame.class, true);
|
||||
KeyPath relPath = matcher.applyKeys(KeyPath.makeIndex(level)).getSingletonPath();
|
||||
KeyPath relPath = filter.applyKeys(KeyPath.makeIndex(level)).getSingletonPath();
|
||||
if (relPath == null) {
|
||||
throw new IllegalStateException("Could not determine where to create new frame");
|
||||
}
|
||||
|
|
|
@ -32,7 +32,8 @@ import ghidra.trace.model.Lifespan;
|
|||
import ghidra.trace.model.stack.*;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.trace.model.target.iface.TraceObjectInterface;
|
||||
import ghidra.trace.model.target.path.*;
|
||||
import ghidra.trace.model.target.path.KeyPath;
|
||||
import ghidra.trace.model.target.path.PathFilter;
|
||||
import ghidra.trace.model.thread.TraceObjectThread;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.util.TraceChangeRecord;
|
||||
|
@ -97,12 +98,12 @@ public class DBTraceStackManager implements TraceStackManager, DBTraceManager {
|
|||
|
||||
public static PathFilter single(TraceObject seed,
|
||||
Class<? extends TraceObjectInterface> targetIf) {
|
||||
PathMatcher stackMatcher = seed.getSchema().searchFor(targetIf, false);
|
||||
if (stackMatcher.getSingletonPath() == null) {
|
||||
PathFilter stackFilter = seed.getSchema().searchFor(targetIf, false);
|
||||
if (stackFilter.getSingletonPath() == null) {
|
||||
throw new IllegalStateException("Schema doesn't provide a unique " +
|
||||
targetIf.getSimpleName() + " for " + seed.getCanonicalPath());
|
||||
}
|
||||
return stackMatcher.getSingletonPattern();
|
||||
return stackFilter.getSingletonPattern();
|
||||
}
|
||||
|
||||
protected TraceObjectStack doGetOrAddObjectStack(TraceThread thread, long snap,
|
||||
|
|
|
@ -36,7 +36,8 @@ import ghidra.trace.model.target.*;
|
|||
import ghidra.trace.model.target.iface.TraceObjectInterface;
|
||||
import ghidra.trace.model.target.info.TraceObjectInterfaceFactory.Constructor;
|
||||
import ghidra.trace.model.target.info.TraceObjectInterfaceUtils;
|
||||
import ghidra.trace.model.target.path.*;
|
||||
import ghidra.trace.model.target.path.KeyPath;
|
||||
import ghidra.trace.model.target.path.PathFilter;
|
||||
import ghidra.trace.model.target.schema.TraceObjectSchema;
|
||||
import ghidra.trace.util.TraceChangeRecord;
|
||||
import ghidra.trace.util.TraceEvents;
|
||||
|
@ -681,8 +682,8 @@ public class DBTraceObject extends DBAnnotatedObject implements TraceObject {
|
|||
public Stream<? extends TraceObjectValPath> findAncestorsInterface(Lifespan span,
|
||||
Class<? extends TraceObjectInterface> iface) {
|
||||
// This is a sort of meet-in-the-middle. The type search must originate from the root
|
||||
PathMatcher matcher = getManager().getRootSchema().searchFor(iface, false);
|
||||
return getAncestorsRoot(span, matcher);
|
||||
PathFilter filter = getManager().getRootSchema().searchFor(iface, false);
|
||||
return getAncestorsRoot(span, filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -694,8 +695,8 @@ public class DBTraceObject extends DBAnnotatedObject implements TraceObject {
|
|||
|
||||
public TraceObject findOrCreateCanonicalAncestorInterface(
|
||||
Class<? extends TraceObjectInterface> iface) {
|
||||
PathMatcher matcher = getManager().getRootSchema().searchFor(iface, false);
|
||||
return path.streamMatchingAncestry(matcher)
|
||||
PathFilter filter = getManager().getRootSchema().searchFor(iface, false);
|
||||
return path.streamMatchingAncestry(filter)
|
||||
.limit(1)
|
||||
.map(kp -> manager.createObject(kp))
|
||||
.findAny()
|
||||
|
@ -711,9 +712,9 @@ public class DBTraceObject extends DBAnnotatedObject implements TraceObject {
|
|||
public Stream<? extends TraceObject> findCanonicalAncestorsInterface(
|
||||
Class<? extends TraceObjectInterface> iface) {
|
||||
// This is a sort of meet-in-the-middle. The type search must originate from the root
|
||||
PathMatcher matcher = getManager().getRootSchema().searchFor(iface, false);
|
||||
PathFilter filter = getManager().getRootSchema().searchFor(iface, false);
|
||||
try (LockHold hold = manager.trace.lockRead()) {
|
||||
return path.streamMatchingAncestry(matcher)
|
||||
return path.streamMatchingAncestry(filter)
|
||||
.map(kp -> manager.getObjectByCanonicalPath(kp));
|
||||
}
|
||||
}
|
||||
|
@ -741,8 +742,8 @@ public class DBTraceObject extends DBAnnotatedObject implements TraceObject {
|
|||
@Override
|
||||
public Stream<? extends TraceObjectValPath> findSuccessorsInterface(Lifespan span,
|
||||
Class<? extends TraceObjectInterface> iface, boolean requireCanonical) {
|
||||
PathMatcher matcher = getSchema().searchFor(iface, requireCanonical);
|
||||
return getSuccessors(span, matcher).filter(p -> isActuallyInterface(p, iface));
|
||||
PathFilter filter = getSchema().searchFor(iface, requireCanonical);
|
||||
return getSuccessors(span, filter).filter(p -> isActuallyInterface(p, iface));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -469,8 +469,8 @@ public class DBTraceObjectManager implements TraceObjectManager, DBTraceManager
|
|||
if (rootSchema == null) {
|
||||
throw new IllegalStateException("There is no schema. Create a root object.");
|
||||
}
|
||||
PathMatcher matcher = rootSchema.searchFor(iface, true);
|
||||
return getValuePaths(span, matcher).filter(p -> {
|
||||
PathFilter filter = rootSchema.searchFor(iface, true);
|
||||
return getValuePaths(span, filter).filter(p -> {
|
||||
TraceObject object = p.getDestination(getRootObject());
|
||||
if (object == null) {
|
||||
Msg.error(this, "NULL VALUE! " + p.getLastEntry());
|
||||
|
|
|
@ -25,7 +25,7 @@ import ghidra.trace.model.memory.TraceObjectRegister;
|
|||
import ghidra.trace.model.symbol.TraceLabelSymbol;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.trace.model.target.path.KeyPath;
|
||||
import ghidra.trace.model.target.path.PathMatcher;
|
||||
import ghidra.trace.model.target.path.PathFilter;
|
||||
import ghidra.trace.model.target.schema.TraceObjectSchema;
|
||||
|
||||
/**
|
||||
|
@ -192,7 +192,7 @@ public interface TracePlatform {
|
|||
* @param names the possible names of the register on the target
|
||||
* @return the path matcher, possibly empty
|
||||
*/
|
||||
PathMatcher getConventionalRegisterPath(TraceObjectSchema schema, KeyPath path,
|
||||
PathFilter getConventionalRegisterPath(TraceObjectSchema schema, KeyPath path,
|
||||
Collection<String> names);
|
||||
|
||||
/**
|
||||
|
@ -207,7 +207,7 @@ public interface TracePlatform {
|
|||
* @param register the platform register
|
||||
* @return the path matcher, possibly empty
|
||||
*/
|
||||
PathMatcher getConventionalRegisterPath(TraceObjectSchema schema, KeyPath path,
|
||||
PathFilter getConventionalRegisterPath(TraceObjectSchema schema, KeyPath path,
|
||||
Register register);
|
||||
|
||||
/**
|
||||
|
@ -218,7 +218,7 @@ public interface TracePlatform {
|
|||
* @param register the platform register
|
||||
* @return that path matcher, possibly empty, or null if the trace has no root schema
|
||||
*/
|
||||
PathMatcher getConventionalRegisterPath(TraceObject container, Register register);
|
||||
PathFilter getConventionalRegisterPath(TraceObject container, Register register);
|
||||
|
||||
/**
|
||||
* Get the expected path where an object defining the register value would be
|
||||
|
@ -228,7 +228,7 @@ public interface TracePlatform {
|
|||
* @param register the platform register
|
||||
* @return the path matcher, or null if there is no root schema
|
||||
*/
|
||||
PathMatcher getConventionalRegisterPath(AddressSpace overlay, Register register);
|
||||
PathFilter getConventionalRegisterPath(AddressSpace overlay, Register register);
|
||||
|
||||
/**
|
||||
* Add a label the conventionally maps the value of a {@link TraceObjectRegister} in the object
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.trace.model.memory;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import ghidra.pcode.utils.Utils;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.trace.model.target.TraceObjectValue;
|
||||
|
||||
public class RegisterValueConverter {
|
||||
private final TraceObjectValue registerValue;
|
||||
private BigInteger value;
|
||||
private int bitLength = -1;
|
||||
private byte[] be;
|
||||
private byte[] le;
|
||||
|
||||
public RegisterValueConverter(TraceObjectValue registerValue) {
|
||||
this.registerValue = registerValue;
|
||||
}
|
||||
|
||||
public static BigInteger convertValueToBigInteger(Object val) throws RegisterValueException {
|
||||
if (val instanceof String s) {
|
||||
try {
|
||||
return new BigInteger(s, 16);
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
throw new RegisterValueException(
|
||||
"Invalid register value " + s + ". Must be hex digits only.");
|
||||
}
|
||||
}
|
||||
else if (val instanceof byte[] arr) {
|
||||
// NOTE: Reg object values are always big endian
|
||||
return new BigInteger(1, arr);
|
||||
}
|
||||
else if (val instanceof Byte b) {
|
||||
return BigInteger.valueOf(b);
|
||||
}
|
||||
else if (val instanceof Short s) {
|
||||
return BigInteger.valueOf(s);
|
||||
}
|
||||
else if (val instanceof Integer i) {
|
||||
return BigInteger.valueOf(i);
|
||||
}
|
||||
else if (val instanceof Long l) {
|
||||
return BigInteger.valueOf(l);
|
||||
}
|
||||
else if (val instanceof Address a) {
|
||||
return a.getOffsetAsBigInteger();
|
||||
}
|
||||
throw new RegisterValueException(
|
||||
"Cannot convert register value: (" + val.getClass() + ") '" + val + "'");
|
||||
}
|
||||
|
||||
BigInteger convertRegisterValueToBigInteger() throws RegisterValueException {
|
||||
return convertValueToBigInteger(registerValue.getValue());
|
||||
}
|
||||
|
||||
int getRegisterValueBitLength() throws RegisterValueException {
|
||||
Object objBitLength = registerValue.getParent()
|
||||
.getValue(registerValue.getMinSnap(), TraceObjectRegister.KEY_BITLENGTH)
|
||||
.getValue();
|
||||
if (!(objBitLength instanceof Number numBitLength)) {
|
||||
throw new RegisterValueException(
|
||||
"Register length is not numeric: (" + objBitLength.getClass() + ") '" +
|
||||
objBitLength + "'");
|
||||
}
|
||||
return numBitLength.intValue();
|
||||
}
|
||||
|
||||
public BigInteger getValue() throws RegisterValueException {
|
||||
if (value != null) {
|
||||
return value;
|
||||
}
|
||||
return value = convertRegisterValueToBigInteger();
|
||||
}
|
||||
|
||||
int getBitLength() throws RegisterValueException {
|
||||
if (bitLength != -1) {
|
||||
return bitLength;
|
||||
}
|
||||
return bitLength = getRegisterValueBitLength();
|
||||
}
|
||||
|
||||
int getByteLength() throws RegisterValueException {
|
||||
return (getBitLength() + 7) / 8;
|
||||
}
|
||||
|
||||
public byte[] getBytesBigEndian() throws RegisterValueException {
|
||||
if (be != null) {
|
||||
return be;
|
||||
}
|
||||
return be = Utils.bigIntegerToBytes(getValue(), getByteLength(), true);
|
||||
}
|
||||
|
||||
public byte[] getBytesLittleEndian() throws RegisterValueException {
|
||||
if (le != null) {
|
||||
return le;
|
||||
}
|
||||
return le = Utils.bigIntegerToBytes(getValue(), getByteLength(), false);
|
||||
}
|
||||
|
||||
public byte[] getBytes(boolean isBigEndian) throws RegisterValueException {
|
||||
return isBigEndian ? getBytesBigEndian() : getBytesLittleEndian();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.trace.model.memory;
|
||||
|
||||
public class RegisterValueException extends Exception {
|
||||
public RegisterValueException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
|
@ -42,8 +42,8 @@ import ghidra.trace.model.thread.TraceObjectThread;
|
|||
* a preferred presentation. In the tree convention, each register is presented with this interface.
|
||||
* The name is taken from the object key, the length in bits is given in the attribute
|
||||
* {@link #KEY_BITLENGTH}, and the value is given in the attribute
|
||||
* {@link TraceObjectInterface#KEY_VALUE}. Some connectors may present registers as primitive
|
||||
* children of the container, but that convention is discouraged.</li>
|
||||
* {@link TraceObjectInterface#KEY_VALUE}. Alternatively, connectors may present registers as
|
||||
* primitive children of the container.</li>
|
||||
* </ol>
|
||||
*
|
||||
* <p>
|
||||
|
|
|
@ -491,7 +491,8 @@ 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. Aliased keys are excluded; those can't be canonical
|
||||
* anyway.
|
||||
* anyway. By definition, a primitive value is not canonical, even if it is the final value in
|
||||
* the path.
|
||||
*
|
||||
* @param relativeFilter filter on the relative path from this object to desired successors
|
||||
* @return the stream of value paths
|
||||
|
|
|
@ -79,8 +79,8 @@ public interface PathFilter {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Collection<PathPattern> getPatterns() {
|
||||
return List.of();
|
||||
public Set<PathPattern> getPatterns() {
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -263,7 +263,7 @@ public interface PathFilter {
|
|||
*
|
||||
* @return the patterns
|
||||
*/
|
||||
Collection<PathPattern> getPatterns();
|
||||
Set<PathPattern> getPatterns();
|
||||
|
||||
/**
|
||||
* Remove count elements from the right
|
||||
|
|
|
@ -17,24 +17,30 @@ package ghidra.trace.model.target.path;
|
|||
|
||||
import java.util.*;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
public class PathMatcher implements PathFilter {
|
||||
protected static final Set<String> WILD_SINGLETON = Set.of("");
|
||||
|
||||
protected final Set<PathPattern> patterns = new HashSet<>();
|
||||
protected final Set<PathPattern> patterns;
|
||||
|
||||
public void addPattern(KeyPath pattern) {
|
||||
patterns.add(new PathPattern(pattern));
|
||||
public static PathMatcher any(Stream<PathPattern> patterns) {
|
||||
return new PathMatcher(patterns.collect(Collectors.toUnmodifiableSet()));
|
||||
}
|
||||
|
||||
public void addPattern(PathPattern pattern) {
|
||||
patterns.add(pattern);
|
||||
public static PathMatcher any(Collection<PathFilter> filters) {
|
||||
return any(filters.stream().flatMap(f -> f.getPatterns().stream()));
|
||||
}
|
||||
|
||||
public void addAll(PathMatcher matcher) {
|
||||
patterns.addAll(matcher.patterns);
|
||||
public static PathMatcher any(PathFilter... filters) {
|
||||
return any(Stream.of(filters).flatMap(f -> f.getPatterns().stream()));
|
||||
}
|
||||
|
||||
PathMatcher(Set<PathPattern> patterns) {
|
||||
this.patterns = patterns;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -59,19 +65,10 @@ public class PathMatcher implements PathFilter {
|
|||
|
||||
@Override
|
||||
public PathFilter or(PathFilter that) {
|
||||
PathMatcher result = new PathMatcher();
|
||||
result.patterns.addAll(this.patterns);
|
||||
if (that instanceof PathMatcher) {
|
||||
PathMatcher matcher = (PathMatcher) that;
|
||||
result.patterns.addAll(matcher.patterns);
|
||||
}
|
||||
else if (that instanceof PathPattern) {
|
||||
result.patterns.add((PathPattern) that);
|
||||
}
|
||||
else {
|
||||
throw new AssertionError();
|
||||
}
|
||||
return result;
|
||||
Set<PathPattern> patterns = new HashSet<>();
|
||||
patterns.addAll(this.patterns);
|
||||
patterns.addAll(that.getPatterns());
|
||||
return new PathMatcher(Collections.unmodifiableSet(patterns));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -123,7 +120,7 @@ public class PathMatcher implements PathFilter {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Collection<PathPattern> getPatterns() {
|
||||
public Set<PathPattern> getPatterns() {
|
||||
return patterns;
|
||||
}
|
||||
|
||||
|
@ -189,19 +186,19 @@ public class PathMatcher implements PathFilter {
|
|||
|
||||
@Override
|
||||
public PathMatcher applyKeys(Align align, List<String> indices) {
|
||||
PathMatcher result = new PathMatcher();
|
||||
for (PathPattern pat : patterns) {
|
||||
result.addPattern(pat.applyKeys(align, indices));
|
||||
Set<PathPattern> patterns = new HashSet<>();
|
||||
for (PathPattern pat : this.patterns) {
|
||||
patterns.add(pat.applyKeys(align, indices));
|
||||
}
|
||||
return result;
|
||||
return new PathMatcher(Collections.unmodifiableSet(patterns));
|
||||
}
|
||||
|
||||
@Override
|
||||
public PathMatcher removeRight(int count) {
|
||||
PathMatcher result = new PathMatcher();
|
||||
for (PathPattern pat : patterns) {
|
||||
pat.doRemoveRight(count, result);
|
||||
Set<PathPattern> patterns = new HashSet<>();
|
||||
for (PathPattern pat : this.patterns) {
|
||||
pat.doRemoveRight(count, patterns);
|
||||
}
|
||||
return result;
|
||||
return new PathMatcher(Collections.unmodifiableSet(patterns));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,19 +78,7 @@ public class PathPattern implements PathFilter {
|
|||
if (this.equals(that)) {
|
||||
return this;
|
||||
}
|
||||
PathMatcher result = new PathMatcher();
|
||||
result.addPattern(this);
|
||||
if (that instanceof PathPattern) {
|
||||
result.addPattern(this);
|
||||
}
|
||||
else if (that instanceof PathMatcher) {
|
||||
PathMatcher matcher = (PathMatcher) that;
|
||||
result.patterns.addAll(matcher.patterns);
|
||||
}
|
||||
else {
|
||||
throw new AssertionError();
|
||||
}
|
||||
return result;
|
||||
return PathMatcher.any(this, that);
|
||||
}
|
||||
|
||||
public static boolean isWildcard(String pat) {
|
||||
|
@ -190,8 +178,8 @@ public class PathPattern implements PathFilter {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Collection<PathPattern> getPatterns() {
|
||||
return List.of(this);
|
||||
public Set<PathPattern> getPatterns() {
|
||||
return Set.of(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -315,18 +303,18 @@ public class PathPattern implements PathFilter {
|
|||
return result;
|
||||
}
|
||||
|
||||
void doRemoveRight(int count, PathMatcher result) {
|
||||
void doRemoveRight(int count, Set<PathPattern> result) {
|
||||
KeyPath parent = pattern.parent(count);
|
||||
if (parent == null) {
|
||||
return;
|
||||
}
|
||||
result.addPattern(parent);
|
||||
result.add(new PathPattern(parent));
|
||||
}
|
||||
|
||||
@Override
|
||||
public PathMatcher removeRight(int count) {
|
||||
PathMatcher result = new PathMatcher();
|
||||
doRemoveRight(count, result);
|
||||
return result;
|
||||
Set<PathPattern> patterns = new HashSet<>();
|
||||
doRemoveRight(count, patterns);
|
||||
return new PathMatcher(Collections.unmodifiableSet(patterns));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ import ghidra.trace.model.TraceExecutionState;
|
|||
import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.trace.model.target.iface.TraceObjectInterface;
|
||||
import ghidra.trace.model.target.path.KeyPath;
|
||||
import ghidra.trace.model.target.path.PathMatcher;
|
||||
import ghidra.trace.model.target.path.PathFilter;
|
||||
|
||||
/**
|
||||
* The schemas common to all contexts, as they describe the primitive and built-in types.
|
||||
|
@ -202,9 +202,9 @@ public enum PrimitiveTraceObjectSchema implements TraceObjectSchema {
|
|||
}
|
||||
|
||||
@Override
|
||||
public PathMatcher searchFor(Class<? extends TraceObjectInterface> type,
|
||||
public PathFilter searchFor(Class<? extends TraceObjectInterface> type,
|
||||
boolean requireCanonical) {
|
||||
return new PathMatcher();
|
||||
return PathFilter.NONE;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -422,7 +422,7 @@ public interface TraceObjectSchema {
|
|||
* @param requireCanonical only return patterns matching a canonical location for the type
|
||||
* @return a set of patterns where such objects could be found
|
||||
*/
|
||||
default PathMatcher searchFor(Class<? extends TraceObjectInterface> type,
|
||||
default PathFilter searchFor(Class<? extends TraceObjectInterface> type,
|
||||
boolean requireCanonical) {
|
||||
return searchFor(type, KeyPath.ROOT, requireCanonical);
|
||||
}
|
||||
|
@ -439,15 +439,15 @@ public interface TraceObjectSchema {
|
|||
* @param requireCanonical only return patterns matching a canonical location for the type
|
||||
* @return a set of patterns where such objects could be found
|
||||
*/
|
||||
default PathMatcher searchFor(Class<? extends TraceObjectInterface> type, KeyPath prefix,
|
||||
default PathFilter searchFor(Class<? extends TraceObjectInterface> type, KeyPath prefix,
|
||||
boolean requireCanonical) {
|
||||
if (type == TraceObjectInterface.class) {
|
||||
throw new IllegalArgumentException("Must provide a specific interface");
|
||||
}
|
||||
PathMatcher result = new PathMatcher();
|
||||
Private.searchFor(this, result, prefix, true, type, false, requireCanonical,
|
||||
Set<PathPattern> patterns = new HashSet<>();
|
||||
Private.searchFor(this, patterns, prefix, true, type, false, requireCanonical,
|
||||
new HashSet<>());
|
||||
return result;
|
||||
return PathMatcher.any(patterns.stream());
|
||||
}
|
||||
|
||||
class Private {
|
||||
|
@ -574,7 +574,7 @@ public interface TraceObjectSchema {
|
|||
}
|
||||
}
|
||||
|
||||
private static void searchFor(TraceObjectSchema sch, PathMatcher result,
|
||||
private static void searchFor(TraceObjectSchema sch, Set<PathPattern> patterns,
|
||||
KeyPath prefix, boolean parentIsCanonical,
|
||||
Class<? extends TraceObjectInterface> type,
|
||||
boolean requireAggregate, boolean requireCanonical,
|
||||
|
@ -583,7 +583,7 @@ public interface TraceObjectSchema {
|
|||
return;
|
||||
}
|
||||
if (sch.getInterfaces().contains(type) && (parentIsCanonical || !requireCanonical)) {
|
||||
result.addPattern(prefix);
|
||||
patterns.add(new PathPattern(prefix));
|
||||
return;
|
||||
}
|
||||
if (!visited.add(sch)) {
|
||||
|
@ -597,24 +597,24 @@ public interface TraceObjectSchema {
|
|||
for (Entry<String, SchemaName> ent : sch.getElementSchemas().entrySet()) {
|
||||
KeyPath extended = prefix.index(ent.getKey());
|
||||
TraceObjectSchema elemSchema = ctx.getSchema(ent.getValue());
|
||||
searchFor(elemSchema, result, extended, isCanonical, type, requireAggregate,
|
||||
searchFor(elemSchema, patterns, extended, isCanonical, type, requireAggregate,
|
||||
requireCanonical, visited);
|
||||
}
|
||||
KeyPath deExtended = prefix.key("[]");
|
||||
TraceObjectSchema deSchema = ctx.getSchema(sch.getDefaultElementSchema());
|
||||
searchFor(deSchema, result, deExtended, isCanonical, type, requireAggregate,
|
||||
searchFor(deSchema, patterns, deExtended, isCanonical, type, requireAggregate,
|
||||
requireCanonical, visited);
|
||||
|
||||
for (Entry<String, AttributeSchema> ent : sch.getAttributeSchemas().entrySet()) {
|
||||
KeyPath extended = prefix.key(ent.getKey());
|
||||
TraceObjectSchema attrSchema = ctx.getSchema(ent.getValue().getSchema());
|
||||
searchFor(attrSchema, result, extended, isCanonical, type, requireAggregate,
|
||||
searchFor(attrSchema, patterns, extended, isCanonical, type, requireAggregate,
|
||||
requireCanonical, visited);
|
||||
}
|
||||
KeyPath daExtended = prefix.key("");
|
||||
TraceObjectSchema daSchema =
|
||||
ctx.getSchema(sch.getDefaultAttributeSchema().getSchema());
|
||||
searchFor(daSchema, result, daExtended, isCanonical, type, requireAggregate,
|
||||
searchFor(daSchema, patterns, daExtended, isCanonical, type, requireAggregate,
|
||||
requireCanonical, visited);
|
||||
|
||||
visited.remove(sch);
|
||||
|
@ -803,14 +803,14 @@ public interface TraceObjectSchema {
|
|||
* @return the filter for finding objects
|
||||
*/
|
||||
default PathFilter filterForSuitable(Class<? extends TraceObjectInterface> type, KeyPath path) {
|
||||
PathMatcher result = new PathMatcher();
|
||||
Set<PathPattern> patterns = new HashSet<>();
|
||||
Set<TraceObjectSchema> visited = new HashSet<>();
|
||||
List<TraceObjectSchema> schemas = getSuccessorSchemas(path);
|
||||
for (; path != null; path = path.parent()) {
|
||||
TraceObjectSchema schema = schemas.get(path.size());
|
||||
Private.searchFor(schema, result, path, false, type, true, false, visited);
|
||||
Private.searchFor(schema, patterns, path, false, type, true, false, visited);
|
||||
}
|
||||
return result;
|
||||
return PathMatcher.any(patterns.stream());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1036,7 +1036,7 @@ public interface TraceObjectSchema {
|
|||
return null;
|
||||
}
|
||||
|
||||
PathMatcher result = new PathMatcher();
|
||||
Set<PathPattern> patterns = new HashSet<>();
|
||||
for (String index : List.of(Integer.toString(frameLevel),
|
||||
"0x" + Integer.toHexString(frameLevel))) {
|
||||
KeyPath framePathRelStack =
|
||||
|
@ -1044,10 +1044,10 @@ public interface TraceObjectSchema {
|
|||
KeyPath framePath = stackPath.extend(framePathRelStack);
|
||||
KeyPath regsPath = searchForSuitable(TraceObjectRegisterContainer.class, framePath);
|
||||
if (regsPath != null) {
|
||||
result.addPattern(regsPath);
|
||||
patterns.add(new PathPattern(regsPath));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
return PathMatcher.any(patterns.stream());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -37,7 +37,7 @@ import ghidra.trace.model.memory.TraceObjectRegister;
|
|||
import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.trace.model.target.TraceObject.ConflictResolution;
|
||||
import ghidra.trace.model.target.path.KeyPath;
|
||||
import ghidra.trace.model.target.path.PathMatcher;
|
||||
import ghidra.trace.model.target.path.PathFilter;
|
||||
import ghidra.trace.model.target.schema.SchemaContext;
|
||||
import ghidra.trace.model.target.schema.TraceObjectSchema.SchemaName;
|
||||
import ghidra.trace.model.target.schema.XmlSchemaContext;
|
||||
|
@ -428,9 +428,9 @@ public class DBTraceObjectRegisterSupportTest extends AbstractGhidraHeadlessInte
|
|||
x86.getConventionalRegisterRange(overlay, EAX));
|
||||
}
|
||||
|
||||
protected static void assertMatches(String path, PathMatcher matcher) {
|
||||
String message = matcher + " does not match " + path;
|
||||
assertTrue(message, matcher.matches(KeyPath.parse(path)));
|
||||
protected static void assertMatches(String path, PathFilter filter) {
|
||||
String message = filter + " does not match " + path;
|
||||
assertTrue(message, filter.matches(KeyPath.parse(path)));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -475,10 +475,10 @@ public class DBTraceObjectRegisterSupportTest extends AbstractGhidraHeadlessInte
|
|||
.createOverlayAddressSpace("Targets[0].Threads[0].Registers", registers);
|
||||
}
|
||||
|
||||
PathMatcher matcher = b.host.getConventionalRegisterPath(overlay, r0);
|
||||
assertMatches("Targets[0].Threads[0].Registers.User[r0]", matcher);
|
||||
assertMatches("Targets[0].Threads[0].Registers.User[a0]", matcher);
|
||||
assertMatches("Targets[0].Threads[0].Registers.User[R0]", matcher);
|
||||
assertMatches("Targets[0].Threads[0].Registers.User[A0]", matcher);
|
||||
PathFilter filter = b.host.getConventionalRegisterPath(overlay, r0);
|
||||
assertMatches("Targets[0].Threads[0].Registers.User[r0]", filter);
|
||||
assertMatches("Targets[0].Threads[0].Registers.User[a0]", filter);
|
||||
assertMatches("Targets[0].Threads[0].Registers.User[R0]", filter);
|
||||
assertMatches("Targets[0].Threads[0].Registers.User[A0]", filter);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -569,6 +569,13 @@ public class DBTraceObjectManagerTest extends AbstractGhidraHeadlessIntegrationT
|
|||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCanonicalSuccessors() {
|
||||
populateModel(3);
|
||||
assertEquals(3, root.getCanonicalSuccessors(PathFilter.parse("Targets[]")).count());
|
||||
assertEquals(0, root.getCanonicalSuccessors(PathFilter.parse("anAttribute")).count());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetValue_TruncatesOrDeletes() {
|
||||
try (Transaction tx = b.startTransaction()) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue