GP-571: dbgeng schema implementation

This commit is contained in:
d-millar 2021-01-07 19:16:39 +00:00 committed by Dan
parent eb66a90f6c
commit c81a17405d
49 changed files with 397 additions and 80 deletions

View file

@ -22,14 +22,14 @@ import java.util.concurrent.CompletableFuture;
import ghidra.async.AsyncUtils;
import ghidra.async.TypeSpec;
import ghidra.dbg.attributes.TargetObjectRef;
import ghidra.dbg.error.DebuggerModelNoSuchPathException;
import ghidra.dbg.error.DebuggerModelTypeException;
import ghidra.dbg.error.*;
import ghidra.dbg.target.TargetMemory;
import ghidra.dbg.target.TargetObject;
import ghidra.dbg.target.schema.EnumerableTargetObjectSchema;
import ghidra.dbg.target.schema.TargetObjectSchema;
import ghidra.dbg.util.PathUtils;
import ghidra.program.model.address.*;
import ghidra.util.Msg;
/**
* A debugger model, often a connection to an external debugger
@ -220,6 +220,7 @@ public interface DebuggerObjectModel {
/**
* Create a reference to the given path in this model
*
* <p>
* Note that the path is not checked until the object is fetched. Thus, it is possible for a
* reference to refer to a non-existent object.
*
@ -238,6 +239,7 @@ public interface DebuggerObjectModel {
/**
* Fetch the attributes of a given model path
*
* <p>
* Giving an empty path will retrieve the attributes of the root object. If the path does not
* exist, the future completes with {@code null}.
*
@ -269,6 +271,7 @@ public interface DebuggerObjectModel {
/**
* Fetch the elements of a given model path
*
* <p>
* Giving an empty path will retrieve all the top-level objects, i.e., elements of the root. If
* the path does not exist, the future completes with {@code null}.
*
@ -300,6 +303,7 @@ public interface DebuggerObjectModel {
/**
* Fetch the root object of the model
*
* <p>
* The root is a virtual object to contain all the top-level objects of the model tree. This
* object represents the debugger itself.
*
@ -310,7 +314,6 @@ public interface DebuggerObjectModel {
/**
* Fetch the value at the given path
*
*
* @param path the path of the value
* @return a future completing with the value or with {@code null} if the path does not exist
*/
@ -462,6 +465,7 @@ public interface DebuggerObjectModel {
/**
* Invalidate the caches for every object known locally.
*
* <p>
* Unlike, {@link TargetObject#invalidateCaches()}, this does not push the request to a remote
* object. If the objects are proxies, just the proxies' caches are cleared. Again, this does
* not apply to caches for the objects' children.
@ -471,10 +475,30 @@ public interface DebuggerObjectModel {
/**
* Close the session and dispose the model
*
* <p>
* For local sessions, terminate the debugger. For client sessions, disconnect.
*
* @return a future which completes when the session is closed
*/
public CompletableFuture<Void> close();
/**
* A convenience for reporting errors conditionally
*
* <p>
* If the message is ignorable, e.g., a {@link DebuggerModelTerminatingException}, then the
* report will be reduced to a stack-free warning.
*
* @param origin the object producing the error
* @param message the error message
* @param ex the exception
*/
default void reportError(Object origin, String message, Throwable ex) {
if (ex == null || DebuggerModelTerminatingException.isIgnorable(ex)) {
Msg.warn(origin, message + ": " + ex);
}
else {
Msg.error(origin, message, ex);
}
}
}

View file

@ -50,9 +50,6 @@ public interface TargetEnvironment<T extends TargetEnvironment<T>> extends Typed
String DEBUGGER_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "debugger";
String OS_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "os";
String ENDIAN_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "endian";
String VISIBLE_ARCH_ATTRIBUTE_NAME = "arch";
String VISIBLE_OS_ATTRIBUTE_NAME = "os";
String VISIBLE_ENDIAN_ATTRIBUTE_NAME = "endian";
/**
* Get a description of the target architecture
@ -66,9 +63,9 @@ public interface TargetEnvironment<T extends TargetEnvironment<T>> extends Typed
*
* @return the target architecture
*/
@TargetAttributeType(name = VISIBLE_ARCH_ATTRIBUTE_NAME)
@TargetAttributeType(name = ARCH_ATTRIBUTE_NAME, hidden = true)
default String getArchitecture() {
return getTypedAttributeNowByName(VISIBLE_ARCH_ATTRIBUTE_NAME, String.class, "");
return getTypedAttributeNowByName(ARCH_ATTRIBUTE_NAME, String.class, "");
}
/**
@ -101,9 +98,9 @@ public interface TargetEnvironment<T extends TargetEnvironment<T>> extends Typed
*
* @return the target operating system
*/
@TargetAttributeType(name = VISIBLE_OS_ATTRIBUTE_NAME)
@TargetAttributeType(name = OS_ATTRIBUTE_NAME, hidden = true)
default String getOperatingSystem() {
return getTypedAttributeNowByName(VISIBLE_OS_ATTRIBUTE_NAME, String.class, "");
return getTypedAttributeNowByName(OS_ATTRIBUTE_NAME, String.class, "");
}
/**
@ -116,9 +113,9 @@ public interface TargetEnvironment<T extends TargetEnvironment<T>> extends Typed
*
* @return the target endianness
*/
@TargetAttributeType(name = VISIBLE_ENDIAN_ATTRIBUTE_NAME)
@TargetAttributeType(name = ENDIAN_ATTRIBUTE_NAME, hidden = true)
default String getEndian() {
return getTypedAttributeNowByName(VISIBLE_ENDIAN_ATTRIBUTE_NAME, String.class, "");
return getTypedAttributeNowByName(ENDIAN_ATTRIBUTE_NAME, String.class, "");
}
// TODO: Devices? File System?

View file

@ -39,7 +39,6 @@ public interface TargetModule<T extends TargetModule<T>> extends TypedTargetObje
Class<Private.Cls> tclass = (Class) TargetModule.class;
TypeSpec<TargetModule<?>> TYPE = TypeSpec.auto();
String VISIBLE_RANGE_ATTRIBUTE_NAME = "range";
String RANGE_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "range";
String MODULE_NAME_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "module_name";
@ -54,9 +53,9 @@ public interface TargetModule<T extends TargetModule<T>> extends TypedTargetObje
*
* @return the base address, or {@code null}
*/
@TargetAttributeType(name = VISIBLE_RANGE_ATTRIBUTE_NAME, required = true)
@TargetAttributeType(name = RANGE_ATTRIBUTE_NAME, required = true, hidden = true)
public default AddressRange getRange() {
return getTypedAttributeNowByName(VISIBLE_RANGE_ATTRIBUTE_NAME, AddressRange.class, null);
return getTypedAttributeNowByName(RANGE_ATTRIBUTE_NAME, AddressRange.class, null);
}
/**

View file

@ -221,7 +221,12 @@ public interface TargetObject extends TargetObjectRef {
.collect(Collectors.toList());
}
@Deprecated
/**
* A conventional prefix of hidden attributes defined by the {@code TargetObject} interfaces
*
* <p>
* When the "hidden" field of attributes can be overridden, this prefix should be removed
*/
String PREFIX_INVISIBLE = "_";
String DISPLAY_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "display";

View file

@ -45,7 +45,6 @@ public interface TargetSection<T extends TargetSection<T>> extends TypedTargetOb
String MODULE_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "module";
String RANGE_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "range";
String VISIBLE_RANGE_ATTRIBUTE_NAME = "range";
/**
* Get the module to which this section belongs
@ -66,9 +65,9 @@ public interface TargetSection<T extends TargetSection<T>> extends TypedTargetOb
*
* @return the range
*/
@TargetAttributeType(name = VISIBLE_RANGE_ATTRIBUTE_NAME, required = true, fixed = true)
@TargetAttributeType(name = RANGE_ATTRIBUTE_NAME, required = true, fixed = true)
public default AddressRange getRange() {
return getTypedAttributeNowByName(VISIBLE_RANGE_ATTRIBUTE_NAME, AddressRange.class, null);
return getTypedAttributeNowByName(RANGE_ATTRIBUTE_NAME, AddressRange.class, null);
}
/**

View file

@ -31,6 +31,7 @@ import ghidra.dbg.target.TargetObject;
import ghidra.dbg.target.schema.DefaultTargetObjectSchema.DefaultAttributeSchema;
import ghidra.dbg.target.schema.TargetObjectSchema.AttributeSchema;
import ghidra.dbg.target.schema.TargetObjectSchema.SchemaName;
import ghidra.util.Msg;
import utilities.util.reflection.ReflectionUtilities;
public class AnnotatedSchemaContext extends DefaultSchemaContext {
@ -115,8 +116,7 @@ public class AnnotatedSchemaContext extends DefaultSchemaContext {
return Set.of(TargetObject.class);
}
throw new IllegalArgumentException("Getter " + getter +
" for attribute must return primitive or subclass of " +
TargetObjectRef.class);
" for attribute must return primitive or subclass of " + TargetObjectRef.class);
}
protected final Map<Class<? extends TargetObject>, SchemaName> namesByClass =
@ -126,13 +126,14 @@ public class AnnotatedSchemaContext extends DefaultSchemaContext {
protected SchemaName nameFromAnnotatedClass(Class<? extends TargetObject> cls) {
synchronized (namesByClass) {
TargetObjectSchemaInfo info = cls.getAnnotation(TargetObjectSchemaInfo.class);
if (info == null) {
// TODO: Compile-time validation?
Msg.warn(this, "Class " + cls + " is not annotated with @" +
TargetObjectSchemaInfo.class.getSimpleName());
return EnumerableTargetObjectSchema.OBJECT.getName();
}
return namesByClass.computeIfAbsent(cls, c -> {
TargetObjectSchemaInfo info = cls.getAnnotation(TargetObjectSchemaInfo.class);
if (info == null) {
// TODO: Compile-time validation?
throw new IllegalArgumentException("Class " + cls + " is not annotated with @" +
TargetObjectSchemaInfo.class.getSimpleName());
}
String name = info.name();
if (name.equals("")) {
return new SchemaName(cls.getSimpleName());
@ -166,8 +167,7 @@ public class AnnotatedSchemaContext extends DefaultSchemaContext {
AttributeSchema attrSchema;
try {
attrSchema =
attributeSchemaFromAnnotatedMethod(declCls, method, at);
attrSchema = attributeSchemaFromAnnotatedMethod(declCls, method, at);
}
catch (IllegalArgumentException e) {
throw new IllegalArgumentException(
@ -209,7 +209,7 @@ public class AnnotatedSchemaContext extends DefaultSchemaContext {
if (bounds.size() != 1) {
// TODO: Compile-time validation?
throw new IllegalArgumentException(
"Could not identify unique element class: " + bounds);
"Could not identify unique element class (" + bounds + ") for " + cls);
}
else {
Class<? extends TargetObject> bound = bounds.iterator().next();
@ -219,8 +219,8 @@ public class AnnotatedSchemaContext extends DefaultSchemaContext {
}
catch (IllegalArgumentException e) {
throw new IllegalArgumentException(
"Could not get schema name from bound " + bound + " of " +
cls + ".fetchElements()",
"Could not get schema name from bound " + bound + " of " + cls +
".fetchElements()",
e);
}
builder.setDefaultElementSchema(schemaName);
@ -245,30 +245,27 @@ public class AnnotatedSchemaContext extends DefaultSchemaContext {
}
protected String attributeNameFromBean(String beanName, boolean isBool) {
beanName = isBool
? StringUtils.removeStartIgnoreCase(beanName, "is")
beanName = isBool ? StringUtils.removeStartIgnoreCase(beanName, "is")
: StringUtils.removeStartIgnoreCase(beanName, "get");
if (beanName.equals("")) {
throw new IllegalArgumentException("Attribute getter must have a name");
}
return beanName
.replaceAll("([A-Z]+)([A-Z][a-z])", "$1_$2")
return beanName.replaceAll("([A-Z]+)([A-Z][a-z])", "$1_$2")
.replaceAll("([a-z])([A-Z])", "$1_$2")
.toLowerCase();
}
protected AttributeSchema attributeSchemaFromAnnotation(TargetAttributeType at) {
return new DefaultAttributeSchema(at.name(), nameFromClass(at.type()),
at.required(), at.fixed(), at.hidden());
return new DefaultAttributeSchema(at.name(), nameFromClass(at.type()), at.required(),
at.fixed(), at.hidden());
}
protected AttributeSchema attributeSchemaFromAnnotatedMethod(Class<? extends TargetObject> cls,
Method method, TargetAttributeType at) {
if (method.getParameterCount() != 0) {
// TODO: Compile-time validation?
throw new IllegalArgumentException(
"Non-getter method " + method + " is annotated with @" +
TargetAttributeType.class.getSimpleName());
throw new IllegalArgumentException("Non-getter method " + method +
" is annotated with @" + TargetAttributeType.class.getSimpleName());
}
String name = at.name();
Class<?> ret = method.getReturnType();
@ -276,11 +273,10 @@ public class AnnotatedSchemaContext extends DefaultSchemaContext {
name = attributeNameFromBean(method.getName(),
EnumerableTargetObjectSchema.BOOL.getTypes().contains(ret));
}
SchemaName primitiveName =
EnumerableTargetObjectSchema.nameForPrimitive(ret);
SchemaName primitiveName = EnumerableTargetObjectSchema.nameForPrimitive(ret);
if (primitiveName != null) {
return new DefaultAttributeSchema(name, primitiveName,
at.required(), at.fixed(), at.hidden());
return new DefaultAttributeSchema(name, primitiveName, at.required(), at.fixed(),
at.hidden());
}
Set<Class<? extends TargetObject>> bounds = getBoundsOfObjectAttributeGetter(cls, method);
if (bounds.size() != 1) {