GP-2189: Add FlatDebuggerAPI interface

This commit is contained in:
Dan 2022-08-15 15:18:15 -04:00
parent 58066601fc
commit c7b464a0be
46 changed files with 4619 additions and 129 deletions

View file

@ -25,12 +25,24 @@ import java.util.*;
import ghidra.dbg.target.TargetObject;
import ghidra.util.Msg;
/**
* A model listener that permits {@link AttributeCallback} annotations for convenient callbacks when
* the named attribute changes
*/
public abstract class AnnotatedDebuggerAttributeListener implements DebuggerModelListener {
private static final String ATTR_METHODS =
"@" + AttributeCallback.class.getSimpleName() + "-annotated methods";
private static final String PARAMS_ERR =
ATTR_METHODS + " must accept 2 parameters: (TargetObject, T)";
/**
* Annotation for a method receiving an attribute change callback
*
* <p>
* The annotated method must accept parameters {@code (TargetObject, T)}, where {@code T} is the
* type of the attribute. Currently, very little checks are applied during construction.
* Incorrect use will result in errors during callback invocation.
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
protected @interface AttributeCallback {

View file

@ -374,4 +374,8 @@ public class DebuggerCallbackReorderer implements DebuggerModelListener {
rec.cancel();
}
}
public CompletableFuture<Void> flushEvents() {
return lastEvent.thenApply(v -> v);
}
}

View file

@ -40,12 +40,16 @@ public class TestDebuggerModelBuilder {
public TestTargetRegister testRegisterPC;
public TestTargetRegister testRegisterSP;
protected TestDebuggerObjectModel newModel(String typeHint) {
return new TestDebuggerObjectModel(typeHint);
}
public void createTestModel() {
createTestModel("Session");
}
public void createTestModel(String typeHint) {
testModel = new TestDebuggerObjectModel(typeHint);
testModel = newModel(typeHint);
}
public Address addr(long offset) {

View file

@ -16,15 +16,19 @@
package ghidra.dbg.model;
import java.io.IOException;
import java.util.Set;
import java.util.concurrent.*;
import org.jdom.JDOMException;
import ghidra.dbg.DebuggerModelListener;
import ghidra.dbg.attributes.TargetDataType;
import ghidra.dbg.target.TargetBreakpointSpec.TargetBreakpointKind;
import ghidra.dbg.target.TargetObject;
import ghidra.dbg.target.schema.TargetObjectSchema;
import ghidra.dbg.target.schema.XmlSchemaContext;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.Register;
// TODO: Refactor with other Fake and Test model stuff.
public class TestDebuggerObjectModel extends EmptyDebuggerObjectModel {
@ -60,13 +64,145 @@ public class TestDebuggerObjectModel extends EmptyDebuggerObjectModel {
this("Session");
}
public Executor getClientExecutor() {
return clientExecutor;
public TestDebuggerObjectModel(String rootHint) {
this.session = newTestTargetSession(rootHint);
addModelRoot(session);
}
public TestDebuggerObjectModel(String rootHint) {
this.session = new TestTargetSession(this, rootHint, ROOT_SCHEMA);
addModelRoot(session);
protected TestTargetSession newTestTargetSession(String rootHint) {
return new TestTargetSession(this, rootHint, ROOT_SCHEMA);
}
protected TestTargetEnvironment newTestTargetEnvironment(TestTargetSession session) {
return new TestTargetEnvironment(session);
}
protected TestTargetProcessContainer newTestTargetProcessContainer(TestTargetSession session) {
return new TestTargetProcessContainer(session);
}
protected TestTargetProcess newTestTargetProcess(TestTargetProcessContainer container, int pid,
AddressSpace space) {
return new TestTargetProcess(container, pid, space);
}
protected TestTargetBreakpointContainer newTestTargetBreakpointContainer(
TestTargetProcess process) {
return new TestTargetBreakpointContainer(process);
}
protected TestTargetBreakpoint newTestTargetBreakpoint(TestTargetBreakpointContainer container,
int num, Address address, int length, Set<TargetBreakpointKind> kinds) {
return new TestTargetBreakpoint(container, num, address, length, kinds);
}
protected TestTargetMemory newTestTargetMemory(TestTargetProcess process, AddressSpace space) {
return new TestTargetMemory(process, space);
}
protected TestTargetMemoryRegion newTestTargetMemoryRegion(TestTargetMemory memory, String name,
AddressRange range, String flags) {
return new TestTargetMemoryRegion(memory, name, range, flags);
}
protected TestTargetModuleContainer newTestTargetModuleContainer(TestTargetProcess process) {
return new TestTargetModuleContainer(process);
}
protected TestTargetModule newTestTargetModule(TestTargetModuleContainer container, String name,
AddressRange range) {
return new TestTargetModule(container, name, range);
}
protected TestTargetSectionContainer newTestTargetSectionContainer(TestTargetModule module) {
return new TestTargetSectionContainer(module);
}
protected TestTargetSection newTestTargetSection(TestTargetSectionContainer container,
String name, AddressRange range) {
return new TestTargetSection(container, name, range);
}
protected TestTargetSymbolNamespace newTestTargetSymbolNamespace(TestTargetModule module) {
return new TestTargetSymbolNamespace(module);
}
protected TestTargetSymbol newTestTargetSymbol(TestTargetSymbolNamespace namespace, String name,
Address address, long size, TargetDataType dataType) {
return new TestTargetSymbol(namespace, name, address, size, dataType);
}
protected TestTargetDataTypeNamespace newTestTargetDataTypeNamespace(TestTargetModule module) {
return new TestTargetDataTypeNamespace(module);
}
protected TestTargetTypedefDataType newTestTargetTypedefDataType(
TestTargetDataTypeNamespace namespace, String name, TargetDataType defDataType) {
return new TestTargetTypedefDataType(namespace, name, defDataType);
}
protected TestTargetTypedefDef newTestTargetTypedefDef(TestTargetTypedefDataType typedef,
TargetDataType dataType) {
return new TestTargetTypedefDef(typedef, dataType);
}
protected TestTargetRegisterContainer newTestTargetRegisterContainer(
TestTargetProcess process) {
return new TestTargetRegisterContainer(process);
}
protected TestTargetRegister newTestTargetRegister(TestTargetRegisterContainer container,
Register register) {
return TestTargetRegister.fromLanguageRegister(container, register);
}
protected TestTargetThreadContainer newTestTargetThreadContainer(TestTargetProcess process) {
return new TestTargetThreadContainer(process);
}
protected TestTargetThread newTestTargetThread(TestTargetThreadContainer container, int tid) {
return new TestTargetThread(container, tid);
}
protected TestTargetRegisterBankInThread newTestTargetRegisterBankInThread(
TestTargetThread thread) {
return new TestTargetRegisterBankInThread(thread);
}
protected TestTargetStack newTestTargetStack(TestTargetThread thread) {
return new TestTargetStack(thread);
}
protected TestTargetStackFrameNoRegisterBank newTestTargetStackFrameNoRegisterBank(
TestTargetStack stack, int level, Address pc) {
return new TestTargetStackFrameNoRegisterBank(stack, level, pc);
}
protected TestTargetStackFrameHasRegisterBank newTestTargetStackFrameHasRegisterBank(
TestTargetStack stack, int level, Address pc) {
return new TestTargetStackFrameHasRegisterBank(stack, level, pc);
}
protected TestTargetRegisterBankInFrame newTestTargetRegisterBankInFrame(
TestTargetStackFrameHasRegisterBank frame) {
return new TestTargetRegisterBankInFrame(frame);
}
protected TestTargetStackFrameIsRegisterBank newTestTargetStackFrameIsRegisterBank(
TestTargetStack stack, int level, Address pc) {
return new TestTargetStackFrameIsRegisterBank(stack, level, pc);
}
protected TestTargetInterpreter newTestTargetInterpreter(TestTargetSession session) {
return new TestTargetInterpreter(session);
}
protected TestMimickJavaLauncher newTestMimickJavaLauncher(TestTargetSession session) {
return new TestMimickJavaLauncher(session);
}
public Executor getClientExecutor() {
return clientExecutor;
}
@Override

View file

@ -56,8 +56,9 @@ public class TestTargetBreakpointContainer
@Override
public CompletableFuture<Void> placeBreakpoint(AddressRange range,
Set<TargetBreakpointKind> kinds) {
TestTargetBreakpoint bpt = new TestTargetBreakpoint(this, counter.getAndIncrement(),
range.getMinAddress(), (int) range.getLength(), kinds);
TestTargetBreakpoint bpt =
getModel().newTestTargetBreakpoint(this, counter.getAndIncrement(),
range.getMinAddress(), (int) range.getLength(), kinds);
changeElements(List.of(), List.of(bpt), "Breakpoint Added");
return getModel().future(null);
}

View file

@ -30,7 +30,8 @@ public class TestTargetDataTypeNamespace
public TestTargetTypedefDataType addTypedefDataType(String name,
TargetDataType defDataType) {
TestTargetTypedefDataType dataType = new TestTargetTypedefDataType(this, name, defDataType);
TestTargetTypedefDataType dataType =
getModel().newTestTargetTypedefDataType(this, name, defDataType);
changeElements(List.of(), List.of(dataType), "Added typedef " + name);
return dataType;
}

View file

@ -73,7 +73,8 @@ public class TestTargetMemory
}
public TestTargetMemoryRegion addRegion(String name, AddressRange range, String flags) {
TestTargetMemoryRegion region = new TestTargetMemoryRegion(this, name, range, flags);
TestTargetMemoryRegion region =
getModel().newTestTargetMemoryRegion(this, name, range, flags);
changeElements(List.of(), List.of(region), "Add test region: " + range);
return region;
}

View file

@ -32,9 +32,9 @@ public class TestTargetModule
public TestTargetModule(TestTargetModuleContainer parent, String name, AddressRange range) {
super(parent, PathUtils.makeKey(name), "Module");
sections = new TestTargetSectionContainer(this);
symbols = new TestTargetSymbolNamespace(this);
types = new TestTargetDataTypeNamespace(this);
sections = getModel().newTestTargetSectionContainer(this);
symbols = getModel().newTestTargetSymbolNamespace(this);
types = getModel().newTestTargetDataTypeNamespace(this);
changeAttributes(List.of(), Map.of(
RANGE_ATTRIBUTE_NAME, range,

View file

@ -29,7 +29,7 @@ public class TestTargetModuleContainer
}
public TestTargetModule addModule(String name, AddressRange range) {
TestTargetModule module = new TestTargetModule(this, name, range);
TestTargetModule module = getModel().newTestTargetModule(this, name, range);
changeElements(List.of(), List.of(module), "Add test module: " + name);
return module;
}

View file

@ -35,11 +35,11 @@ public class TestTargetProcess extends
public TestTargetProcess(DefaultTestTargetObject<?, ?> parent, int pid, AddressSpace space) {
super(parent, PathUtils.makeKey(PathUtils.makeIndex(pid)), "Process");
breaks = new TestTargetBreakpointContainer(this);
memory = new TestTargetMemory(this, space);
modules = new TestTargetModuleContainer(this);
regs = new TestTargetRegisterContainer(this);
threads = new TestTargetThreadContainer(this);
breaks = getModel().newTestTargetBreakpointContainer(this);
memory = getModel().newTestTargetMemory(this, space);
modules = getModel().newTestTargetModuleContainer(this);
regs = getModel().newTestTargetRegisterContainer(this);
threads = getModel().newTestTargetThreadContainer(this);
changeAttributes(List.of(), List.of(
breaks,

View file

@ -27,7 +27,7 @@ public class TestTargetProcessContainer
}
public TestTargetProcess addProcess(int pid, AddressSpace space) {
TestTargetProcess proc = new TestTargetProcess(this, pid, space);
TestTargetProcess proc = getModel().newTestTargetProcess(this, pid, space);
changeElements(List.of(), List.of(proc), Map.of(), "Test Process Added");
return proc;
}

View file

@ -41,14 +41,14 @@ public class TestTargetRegisterContainer
if (!predicate.test(register)) {
continue;
}
add.add(TestTargetRegister.fromLanguageRegister(this, register));
add.add(getModel().newTestTargetRegister(this, register));
}
changeElements(List.of(), add, "Added registers from Ghidra language: " + language);
return add;
}
public TestTargetRegister addRegister(Register register) {
TestTargetRegister tr = TestTargetRegister.fromLanguageRegister(this, register);
TestTargetRegister tr = getModel().newTestTargetRegister(this, register);
changeElements(List.of(), List.of(tr), "Added " + register + " from Ghidra language");
return tr;
}

View file

@ -29,7 +29,7 @@ public class TestTargetSectionContainer
}
public TestTargetSection addSection(String name, AddressRange range) {
TestTargetSection section = new TestTargetSection(this, name, range);
TestTargetSection section = getModel().newTestTargetSection(this, name, range);
changeElements(List.of(), List.of(section), "Add test section: " + name);
return section;
}

View file

@ -42,10 +42,10 @@ public class TestTargetSession extends DefaultTargetModelRoot
public TestTargetSession(TestDebuggerObjectModel model, String rootHint,
TargetObjectSchema schema) {
super(model, rootHint, schema);
environment = new TestTargetEnvironment(this);
processes = new TestTargetProcessContainer(this);
interpreter = new TestTargetInterpreter(this);
mimickJavaLauncher = new TestMimickJavaLauncher(this);
environment = model.newTestTargetEnvironment(this);
processes = model.newTestTargetProcessContainer(this);
interpreter = model.newTestTargetInterpreter(this);
mimickJavaLauncher = model.newTestMimickJavaLauncher(this);
changeAttributes(List.of(),
List.of(environment, processes, interpreter, mimickJavaLauncher), Map.of(),

View file

@ -39,7 +39,8 @@ public class TestTargetStack extends DefaultTestTargetObject<TestTargetStackFram
}
public TestTargetStackFrameNoRegisterBank pushFrameNoBank(Address pc) {
return pushFrame(new TestTargetStackFrameNoRegisterBank(this, elements.size(), pc));
return pushFrame(
getModel().newTestTargetStackFrameNoRegisterBank(this, elements.size(), pc));
}
/**
@ -48,7 +49,8 @@ public class TestTargetStack extends DefaultTestTargetObject<TestTargetStackFram
* @return the "new" highest-indexed frame, into which old data was pushed
*/
public TestTargetStackFrameHasRegisterBank pushFrameHasBank(Address pc) {
return pushFrame(new TestTargetStackFrameHasRegisterBank(this, elements.size(), pc));
return pushFrame(
getModel().newTestTargetStackFrameHasRegisterBank(this, elements.size(), pc));
}
/**
@ -57,6 +59,7 @@ public class TestTargetStack extends DefaultTestTargetObject<TestTargetStackFram
* @return the "new" highest-indexed frame, into which old data was pushed
*/
public TestTargetStackFrameIsRegisterBank pushFrameIsBank(Address pc) {
return pushFrame(new TestTargetStackFrameIsRegisterBank(this, elements.size(), pc));
return pushFrame(
getModel().newTestTargetStackFrameIsRegisterBank(this, elements.size(), pc));
}
}

View file

@ -29,7 +29,7 @@ public class TestTargetStackFrameHasRegisterBank
public TestTargetStackFrameHasRegisterBank(TestTargetStack parent, int level, Address pc) {
super(parent, PathUtils.makeKey(PathUtils.makeIndex(level)), "Frame");
bank = new TestTargetRegisterBankInFrame(this);
bank = getModel().newTestTargetRegisterBankInFrame(this);
changeAttributes(List.of(), Map.of(
bank.getName(), bank, //

View file

@ -31,7 +31,8 @@ public class TestTargetSymbolNamespace
public TestTargetSymbol addSymbol(String name, Address address, long size,
TargetDataType dataType) {
TestTargetSymbol symbol = new TestTargetSymbol(this, name, address, size, dataType);
TestTargetSymbol symbol =
getModel().newTestTargetSymbol(this, name, address, size, dataType);
changeElements(List.of(), List.of(symbol), "Added symbol " + name);
return symbol;
}

View file

@ -17,20 +17,26 @@ package ghidra.dbg.model;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import ghidra.dbg.target.TargetExecutionStateful;
import ghidra.dbg.target.TargetThread;
import ghidra.dbg.util.CollectionUtils.Delta;
import ghidra.async.AsyncUtils;
import ghidra.dbg.target.*;
import ghidra.dbg.util.PathUtils;
public class TestTargetThread
extends DefaultTestTargetObject<TestTargetObject, TestTargetThreadContainer>
implements TargetThread, TargetExecutionStateful {
implements TargetThread, TargetExecutionStateful, TargetSteppable, TargetResumable,
TargetInterruptible, TargetKillable {
public static final TargetStepKindSet SUPPORTED_KINDS =
TargetStepKindSet.of(TargetStepKind.values());
public TestTargetThread(TestTargetThreadContainer parent, int tid) {
super(parent, PathUtils.makeKey(PathUtils.makeIndex(tid)), "Thread");
changeAttributes(List.of(), List.of(), Map.of(
STATE_ATTRIBUTE_NAME, TargetExecutionState.STOPPED //
), "Initialized");
STATE_ATTRIBUTE_NAME, TargetExecutionState.STOPPED,
SUPPORTED_STEP_KINDS_ATTRIBUTE_NAME, SUPPORTED_KINDS),
"Initialized");
}
/**
@ -39,7 +45,7 @@ public class TestTargetThread
* @return the created register bank
*/
public TestTargetRegisterBankInThread addRegisterBank() {
TestTargetRegisterBankInThread regs = new TestTargetRegisterBankInThread(this);
TestTargetRegisterBankInThread regs = getModel().newTestTargetRegisterBankInThread(this);
changeAttributes(List.of(), List.of(
regs),
Map.of(), "Add Test Register Bank");
@ -47,7 +53,7 @@ public class TestTargetThread
}
public TestTargetStack addStack() {
TestTargetStack stack = new TestTargetStack(this);
TestTargetStack stack = getModel().newTestTargetStack(this);
changeAttributes(List.of(), List.of(
stack),
Map.of(), "Add Test Stack");
@ -55,8 +61,28 @@ public class TestTargetThread
}
public void setState(TargetExecutionState state) {
Delta<?, ?> delta = changeAttributes(List.of(), List.of(), Map.of(
STATE_ATTRIBUTE_NAME, state //
), "Changed state");
changeAttributes(List.of(), List.of(), Map.of(
STATE_ATTRIBUTE_NAME, state),
"Changed state");
}
@Override
public CompletableFuture<Void> step(TargetStepKind kind) {
return AsyncUtils.NIL;
}
@Override
public CompletableFuture<Void> resume() {
return AsyncUtils.NIL;
}
@Override
public CompletableFuture<Void> interrupt() {
return AsyncUtils.NIL;
}
@Override
public CompletableFuture<Void> kill() {
return AsyncUtils.NIL;
}
}

View file

@ -24,12 +24,13 @@ import ghidra.dbg.target.TargetObject;
public class TestTargetThreadContainer
extends DefaultTestTargetObject<TestTargetThread, TestTargetProcess> {
public TestTargetThreadContainer(TestTargetProcess parent) {
super(parent, "Threads", "Threads");
}
public TestTargetThread addThread(int tid) {
TestTargetThread thread = new TestTargetThread(this, tid);
TestTargetThread thread = getModel().newTestTargetThread(this, tid);
changeElements(List.of(), List.of(thread), Map.of(), "Test Thread Added");
return thread;
}

View file

@ -21,9 +21,11 @@ import ghidra.dbg.attributes.TargetDataType;
public class TestTargetTypedefDataType
extends TestTargetNamedDataType<TestTargetTypedefDef> {
public TestTargetTypedefDataType(TestTargetDataTypeNamespace parent, String name,
TargetDataType dataType) {
super(parent, name, NamedDataTypeKind.TYPEDEF, "TypedefType");
changeElements(List.of(), List.of(new TestTargetTypedefDef(this, dataType)), "Initialized");
changeElements(List.of(), List.of(getModel().newTestTargetTypedefDef(this, dataType)),
"Initialized");
}
}

View file

@ -172,7 +172,10 @@
<schema name="Thread" elementResync="NEVER" attributeResync="NEVER">
<interface name="Thread" />
<interface name="ExecutionStateful" />
<!--interface name="Steppable" / -->
<interface name="Steppable" />
<interface name="Resumable" />
<interface name="Interruptible" />
<interface name="Killable" />
<element schema="VOID" />
<attribute name="_tid" schema="INT" hidden="yes" />
<attribute name="_modified" schema="BOOL" hidden="yes" />