mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-28 18:10:24 +08:00
GP-0: Fixing test fallout from GP-690
This commit is contained in:
@@ -267,8 +267,8 @@ public class GadpClientServerTest {
|
||||
return processes;
|
||||
}
|
||||
|
||||
public void addLinks() {
|
||||
assertNotNull(available.fetchElements().getNow(null));
|
||||
public void addLinks() throws Throwable {
|
||||
waitOn(available.fetchElements());
|
||||
links.setElements(List.of(), Map.of(
|
||||
"1", available.getCachedElements().get("2"),
|
||||
"2", available.getCachedElements().get("1")),
|
||||
@@ -309,7 +309,7 @@ public class GadpClientServerTest {
|
||||
}
|
||||
Object ret = method.testInvoke(invocation.getValue());
|
||||
changeAttributes(List.of(), Map.of(name, ret), "Invoked " + name);
|
||||
return CompletableFuture.completedFuture(ret);
|
||||
return CompletableFuture.completedFuture(ret).thenCompose(model::gateFuture);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -338,8 +338,7 @@ public class GadpClientServerTest {
|
||||
implements TargetMethod<TestTargetMethod> {
|
||||
private Function<String, ?> method;
|
||||
|
||||
public TestTargetMethod(TargetObject parent, String key,
|
||||
Function<String, ?> method) {
|
||||
public TestTargetMethod(TargetObject parent, String key, Function<String, ?> method) {
|
||||
super(parent.getModel(), parent, key, "Method");
|
||||
this.method = method;
|
||||
|
||||
@@ -401,8 +400,7 @@ public class GadpClientServerTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> requestElements(
|
||||
boolean refresh) {
|
||||
public CompletableFuture<Void> requestElements(boolean refresh) {
|
||||
setElements(List.of(
|
||||
new TestGadpTargetAvailable(this, 1, "echo"),
|
||||
new TestGadpTargetAvailable(this, 2, "dd")),
|
||||
@@ -619,7 +617,7 @@ public class GadpClientServerTest {
|
||||
TargetMethod<?> method = waitOn(methodRef.as(TargetMethod.tclass).fetch());
|
||||
assertNotNull(method);
|
||||
assertEquals("Hello, World!", waitOn(avail.fetchAttribute("greet(World)")));
|
||||
runner.server.model.session.available.punct = '?';
|
||||
runner.server.model.session.available.punct = '?'; // No effect before flush
|
||||
assertEquals("Hello, World!", waitOn(avail.fetchAttribute("greet(World)")));
|
||||
|
||||
// Flush the cache
|
||||
@@ -746,6 +744,7 @@ public class GadpClientServerTest {
|
||||
(TargetObjectRef) attrs.get(TargetFocusScope.FOCUS_ATTRIBUTE_NAME);
|
||||
// NOTE: Could be actual object, but need not be
|
||||
assertEquals(List.of("Processes", "[0]"), ref.getPath());
|
||||
waitOn(focusPath);
|
||||
waitOn(client.close());
|
||||
|
||||
assertFalse(failed.get());
|
||||
|
||||
+21
-13
@@ -60,18 +60,17 @@ import ghidra.util.classfinder.ClassSearcher;
|
||||
import ghidra.util.datastruct.CollectionChangeListener;
|
||||
import ghidra.util.datastruct.ListenerSet;
|
||||
|
||||
@PluginInfo( //
|
||||
shortDescription = "Debugger models manager service", //
|
||||
description = "Manage debug sessions, connections, and trace recording", //
|
||||
category = PluginCategoryNames.DEBUGGER, //
|
||||
packageName = DebuggerPluginPackage.NAME, //
|
||||
status = PluginStatus.HIDDEN, //
|
||||
servicesRequired = { //
|
||||
}, //
|
||||
servicesProvided = { //
|
||||
DebuggerModelService.class, //
|
||||
} //
|
||||
)
|
||||
@PluginInfo(
|
||||
shortDescription = "Debugger models manager service",
|
||||
description = "Manage debug sessions, connections, and trace recording",
|
||||
category = PluginCategoryNames.DEBUGGER,
|
||||
packageName = DebuggerPluginPackage.NAME,
|
||||
status = PluginStatus.HIDDEN,
|
||||
servicesRequired = {
|
||||
},
|
||||
servicesProvided = {
|
||||
DebuggerModelService.class,
|
||||
})
|
||||
public class DebuggerModelServicePlugin extends Plugin
|
||||
implements DebuggerModelServiceInternal, FrontEndOnly {
|
||||
|
||||
@@ -125,6 +124,9 @@ public class DebuggerModelServicePlugin extends Plugin
|
||||
this.root = r;
|
||||
}
|
||||
r.addListener(this.forRemoval);
|
||||
if (!r.isValid()) {
|
||||
forRemoval.invalidated(root, "Who knows?");
|
||||
}
|
||||
CompletableFuture<? extends TargetFocusScope<?>> findSuitable =
|
||||
DebugModelConventions.findSuitable(TargetFocusScope.tclass, r);
|
||||
return findSuitable;
|
||||
@@ -132,7 +134,9 @@ public class DebuggerModelServicePlugin extends Plugin
|
||||
synchronized (this) {
|
||||
this.focusScope = fs;
|
||||
}
|
||||
fs.addListener(this.forFocus);
|
||||
if (fs != null) {
|
||||
fs.addListener(this.forFocus);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -436,6 +440,10 @@ public class DebuggerModelServicePlugin extends Plugin
|
||||
|
||||
@Override
|
||||
public synchronized DebuggerObjectModel getCurrentModel() {
|
||||
if (!currentModel.isAlive()) {
|
||||
currentModel = null;
|
||||
|
||||
}
|
||||
return currentModel;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,20 @@ import ghidra.program.model.lang.Language;
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
|
||||
/**
|
||||
* An executor which can perform (some of) its work asynchronously
|
||||
*
|
||||
* <p>
|
||||
* Note that a future returned from, e.g., {@link #executeAsync(SleighProgram, SleighUseropLibrary)}
|
||||
* may complete before the computation has actually been performed. They complete when all of the
|
||||
* operations have been scheduled, and the last future has been written into the state. (This
|
||||
* typically happens when any branch conditions have completed). Instead, a caller should read from
|
||||
* the async state, which will return a future. The state will ensure that future does not complete
|
||||
* until the computation has been performed -- assuming the requested variable actually depends on
|
||||
* that computation.
|
||||
*
|
||||
* @param <T> the type of values in the state
|
||||
*/
|
||||
public class AsyncPcodeExecutor<T> extends PcodeExecutor<CompletableFuture<T>> {
|
||||
public AsyncPcodeExecutor(Language language, PcodeArithmetic<CompletableFuture<T>> behavior,
|
||||
PcodeExecutorStatePiece<CompletableFuture<T>, CompletableFuture<T>> state) {
|
||||
|
||||
+1
-2
@@ -52,8 +52,7 @@ public class AsyncWrappedPcodeExecutorStatePiece<A, T>
|
||||
}
|
||||
|
||||
protected CompletableFuture<T> doGetVar(AddressSpace space, CompletableFuture<A> offset,
|
||||
int size,
|
||||
boolean truncateAddressableUnit) {
|
||||
int size, boolean truncateAddressableUnit) {
|
||||
return offset.thenApply(off -> {
|
||||
return state.getVar(space, off, size, truncateAddressableUnit);
|
||||
});
|
||||
|
||||
+12
-8
@@ -35,6 +35,7 @@ import ghidra.dbg.model.TestTargetMemoryRegion;
|
||||
import ghidra.dbg.model.TestTargetProcess;
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.dbg.target.TargetBreakpointSpec.TargetBreakpointKind;
|
||||
import ghidra.dbg.util.DebuggerModelTestUtils;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Bookmark;
|
||||
import ghidra.program.model.listing.Program;
|
||||
@@ -49,7 +50,8 @@ import ghidra.util.SystemUtilities;
|
||||
import ghidra.util.database.UndoableTransaction;
|
||||
import ghidra.util.datastruct.ListenerMap;
|
||||
|
||||
public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDebuggerGUITest {
|
||||
public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDebuggerGUITest
|
||||
implements DebuggerModelTestUtils {
|
||||
protected static final long TIMEOUT_MILLIS =
|
||||
SystemUtilities.isInTestingBatchMode() ? 5000 : Long.MAX_VALUE;
|
||||
|
||||
@@ -1261,7 +1263,7 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPlaceDisableStepThenEnableTraceOnly() throws Exception {
|
||||
public void testPlaceDisableStepThenEnableTraceOnly() throws Throwable {
|
||||
startRecorder1();
|
||||
Trace trace = recorder1.getTrace();
|
||||
traceManager.openTrace(trace);
|
||||
@@ -1274,24 +1276,25 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe
|
||||
assertLogicalBreakpointForLoneSoftwareBreakpoint(trace);
|
||||
|
||||
LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints());
|
||||
lb.disable();
|
||||
waitOn(lb.disable());
|
||||
waitForDomainObject(trace);
|
||||
assertEquals(Enablement.DISABLED, lb.computeEnablement());
|
||||
|
||||
// Simulate a step, which should also cause snap advance in recorder
|
||||
long oldSnap = recorder1.getSnap();
|
||||
mb.testModel.session.simulateStep(mb.testThread1);
|
||||
waitOn(mb.testModel.getClientExecutor());
|
||||
assertEquals(oldSnap + 1, recorder1.getSnap());
|
||||
|
||||
assertEquals(Enablement.DISABLED, lb.computeEnablement());
|
||||
|
||||
lb.enable();
|
||||
waitOn(lb.enable());
|
||||
waitForDomainObject(trace);
|
||||
assertEquals(Enablement.ENABLED, lb.computeEnablement());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteBreakpointTraceOnly() throws Exception {
|
||||
public void testDeleteBreakpointTraceOnly() throws Throwable {
|
||||
startRecorder1();
|
||||
Trace trace = recorder1.getTrace();
|
||||
traceManager.openTrace(trace);
|
||||
@@ -1305,14 +1308,14 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe
|
||||
|
||||
LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints());
|
||||
|
||||
lb.delete();
|
||||
waitOn(lb.delete());
|
||||
waitForDomainObject(trace);
|
||||
|
||||
assertTrue(breakpointService.getAllBreakpoints().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPlaceStepThenDeleteBreakpointTraceOnly() throws Exception {
|
||||
public void testPlaceStepThenDeleteBreakpointTraceOnly() throws Throwable {
|
||||
startRecorder1();
|
||||
Trace trace = recorder1.getTrace();
|
||||
traceManager.openTrace(trace);
|
||||
@@ -1329,9 +1332,10 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe
|
||||
// Simulate a step, which should also cause snap advance in recorder
|
||||
long oldSnap = recorder1.getSnap();
|
||||
mb.testModel.session.simulateStep(mb.testThread1);
|
||||
waitOn(mb.testModel.getClientExecutor());
|
||||
assertEquals(oldSnap + 1, recorder1.getSnap());
|
||||
|
||||
lb.delete();
|
||||
waitOn(lb.delete());
|
||||
waitForDomainObject(trace);
|
||||
|
||||
assertTrue(breakpointService.getAllBreakpoints().isEmpty());
|
||||
|
||||
+29
-19
@@ -33,8 +33,7 @@ import ghidra.dbg.DebuggerModelFactory;
|
||||
import ghidra.dbg.DebuggerObjectModel;
|
||||
import ghidra.dbg.model.TestDebuggerObjectModel;
|
||||
import ghidra.dbg.model.TestLocalDebuggerModelFactory;
|
||||
import ghidra.dbg.util.PathMatcher;
|
||||
import ghidra.dbg.util.PathUtils;
|
||||
import ghidra.dbg.util.*;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.util.SystemUtilities;
|
||||
@@ -47,7 +46,8 @@ import mockit.VerificationsInOrder;
|
||||
*
|
||||
* TODO: Cover cases where multiple recorders are present
|
||||
*/
|
||||
public class DebuggerModelServiceTest extends AbstractGhidraHeadedDebuggerGUITest {
|
||||
public class DebuggerModelServiceTest extends AbstractGhidraHeadedDebuggerGUITest
|
||||
implements DebuggerModelTestUtils {
|
||||
protected static final long TIMEOUT_MILLIS =
|
||||
SystemUtilities.isInTestingBatchMode() ? 5000 : Long.MAX_VALUE;
|
||||
|
||||
@@ -266,20 +266,23 @@ public class DebuggerModelServiceTest extends AbstractGhidraHeadedDebuggerGUITes
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRecordThenCloseStopsRecording() throws Exception {
|
||||
public void testRecordThenCloseStopsRecording() throws Throwable {
|
||||
createTestModel();
|
||||
mb.createTestProcessesAndThreads();
|
||||
|
||||
TraceRecorder recorder = modelService.recordTarget(mb.testProcess1,
|
||||
new TestDebuggerTargetTraceMapper(mb.testProcess1));
|
||||
assertNotNull(recorder);
|
||||
waitOn(recorder.init()); // Already initializing, just wait for it to complete
|
||||
|
||||
CompletableFuture<Void> disconnect = mb.testModel.close();
|
||||
disconnect.get();
|
||||
waitForCondition(() -> !recorder.isRecording(), "Still recording");
|
||||
waitOn(mb.testModel.close());
|
||||
waitForPass(() -> {
|
||||
assertFalse("Still recording", recorder.isRecording());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRecordAndOpenThenCloseModelAndTraceLeavesNoConsumers() throws Exception {
|
||||
public void testRecordAndOpenThenCloseModelAndTraceLeavesNoConsumers() throws Throwable {
|
||||
createTestModel();
|
||||
mb.createTestProcessesAndThreads();
|
||||
|
||||
@@ -291,9 +294,10 @@ public class DebuggerModelServiceTest extends AbstractGhidraHeadedDebuggerGUITes
|
||||
assertNotNull("No active trace", trace);
|
||||
|
||||
traceManager.closeTrace(trace);
|
||||
CompletableFuture<Void> disconnect = mb.testModel.close();
|
||||
disconnect.get();
|
||||
assertEquals(List.of(), trace.getConsumerList());
|
||||
waitOn(mb.testModel.close());
|
||||
waitForPass(() -> {
|
||||
assertEquals(List.of(), trace.getConsumerList());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -359,7 +363,13 @@ public class DebuggerModelServiceTest extends AbstractGhidraHeadedDebuggerGUITes
|
||||
|
||||
TraceThread traceThread = Unique.assertOne(
|
||||
recorder.getTrace().getThreadManager().getThreadsByPath("Processes[1].Threads[1]"));
|
||||
assertEquals(mb.testThread1, modelService.getTargetThread(traceThread));
|
||||
/**
|
||||
* There's a brief period where the trace thread exists, but it hasn't been entered into the
|
||||
* recorder's internal map, yet. So, we have to wait.
|
||||
*/
|
||||
waitForPass(() -> {
|
||||
assertEquals(mb.testThread1, modelService.getTargetThread(traceThread));
|
||||
});
|
||||
}
|
||||
|
||||
protected void doTestGetTraceThread(Runnable preRec, Runnable postRec) throws Exception {
|
||||
@@ -429,7 +439,7 @@ public class DebuggerModelServiceTest extends AbstractGhidraHeadedDebuggerGUITes
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTargetFocus() throws Exception {
|
||||
public void testTargetFocus() throws Throwable {
|
||||
createTestModel();
|
||||
mb.createTestProcessesAndThreads();
|
||||
|
||||
@@ -442,19 +452,19 @@ public class DebuggerModelServiceTest extends AbstractGhidraHeadedDebuggerGUITes
|
||||
assertNull(modelService.getTargetFocus(mb.testProcess1));
|
||||
assertNull(modelService.getTargetFocus(mb.testProcess3));
|
||||
|
||||
mb.testModel.requestFocus(mb.testThread1);
|
||||
waitOn(mb.testModel.requestFocus(mb.testThread1));
|
||||
assertEquals(mb.testThread1, modelService.getTargetFocus(mb.testProcess1));
|
||||
assertNull(modelService.getTargetFocus(mb.testProcess3));
|
||||
|
||||
mb.testModel.requestFocus(mb.testThread2);
|
||||
waitOn(mb.testModel.requestFocus(mb.testThread2));
|
||||
assertEquals(mb.testThread2, modelService.getTargetFocus(mb.testProcess1));
|
||||
assertNull(modelService.getTargetFocus(mb.testProcess3));
|
||||
|
||||
mb.testModel.requestFocus(mb.testThread3);
|
||||
waitOn(mb.testModel.requestFocus(mb.testThread3));
|
||||
assertEquals(mb.testThread2, modelService.getTargetFocus(mb.testProcess1));
|
||||
assertEquals(mb.testThread3, modelService.getTargetFocus(mb.testProcess3));
|
||||
|
||||
mb.testModel.requestFocus(mb.testThread4);
|
||||
waitOn(mb.testModel.requestFocus(mb.testThread4));
|
||||
assertEquals(mb.testThread2, modelService.getTargetFocus(mb.testProcess1));
|
||||
assertEquals(mb.testThread4, modelService.getTargetFocus(mb.testProcess3));
|
||||
}
|
||||
@@ -484,13 +494,13 @@ public class DebuggerModelServiceTest extends AbstractGhidraHeadedDebuggerGUITes
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCurrentModelNullAfterClose() throws Exception {
|
||||
public void testCurrentModelNullAfterClose() throws Throwable {
|
||||
createTestModel();
|
||||
|
||||
modelService.activateModel(mb.testModel);
|
||||
assertEquals(mb.testModel, modelService.getCurrentModel());
|
||||
|
||||
mb.testModel.close();
|
||||
waitOn(mb.testModel.close());
|
||||
assertNull(modelService.getCurrentModel());
|
||||
}
|
||||
}
|
||||
|
||||
+6
-5
@@ -61,8 +61,7 @@ public class TraceRecorderAsyncPcodeExecTest extends AbstractGhidraHeadedDebugge
|
||||
Language language = trace.getBaseLanguage();
|
||||
|
||||
SleighExpression expr = SleighProgramCompiler
|
||||
.compileExpression((SleighLanguage) language,
|
||||
"r0 + r1");
|
||||
.compileExpression((SleighLanguage) language, "r0 + r1");
|
||||
|
||||
AsyncPcodeExecutor<byte[]> executor =
|
||||
new AsyncPcodeExecutor<>(language, AsyncWrappedPcodeArithmetic.forLanguage(language),
|
||||
@@ -94,10 +93,12 @@ public class TraceRecorderAsyncPcodeExecTest extends AbstractGhidraHeadedDebugge
|
||||
SleighProgram prog = SleighProgramCompiler.compileProgram((SleighLanguage) language, "test",
|
||||
List.of("r2 = r0 + r1;"), SleighUseropLibrary.NIL);
|
||||
|
||||
AsyncPcodeExecutor<byte[]> executor =
|
||||
new AsyncPcodeExecutor<>(language, AsyncWrappedPcodeArithmetic.forLanguage(language),
|
||||
new TraceRecorderAsyncPcodeExecutorState(recorder, recorder.getSnap(), thread, 0));
|
||||
TraceRecorderAsyncPcodeExecutorState asyncState =
|
||||
new TraceRecorderAsyncPcodeExecutorState(recorder, recorder.getSnap(), thread, 0);
|
||||
AsyncPcodeExecutor<byte[]> executor = new AsyncPcodeExecutor<>(
|
||||
language, AsyncWrappedPcodeArithmetic.forLanguage(language), asyncState);
|
||||
waitOn(executor.executeAsync(prog, SleighUseropLibrary.nil()));
|
||||
waitOn(asyncState.getVar(language.getRegister("r2")));
|
||||
|
||||
assertEquals(BigInteger.valueOf(11), new BigInteger(1, regs.regVals.get("r2")));
|
||||
}
|
||||
|
||||
@@ -15,8 +15,7 @@
|
||||
*/
|
||||
package ghidra.async;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
import ghidra.util.SystemUtilities;
|
||||
|
||||
@@ -32,4 +31,8 @@ public interface AsyncTestUtils {
|
||||
throw AsyncUtils.unwrapThrowable(e);
|
||||
}
|
||||
}
|
||||
|
||||
default void waitOn(Executor executor) throws Throwable {
|
||||
waitOn(CompletableFuture.supplyAsync(() -> null, executor));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import ghidra.util.classfinder.ExtensionPoint;
|
||||
/**
|
||||
* A factory for a debugger model
|
||||
*
|
||||
* <p>
|
||||
* This provides a discoverable means of creating a debug model.
|
||||
*/
|
||||
public interface DebuggerModelFactory
|
||||
|
||||
@@ -509,4 +509,39 @@ public interface DebuggerObjectModel {
|
||||
* @return the executor
|
||||
*/
|
||||
Executor getClientExecutor();
|
||||
|
||||
/**
|
||||
* Ensure that dependent computations occur on the client executor
|
||||
*
|
||||
* <p>
|
||||
* This also preserves scheduling order on the executor. Using just
|
||||
* {@link CompletableFuture#thenApplyAsync(java.util.function.Function)} makes no guarantees
|
||||
* about execution order, because that invocation could occur before invocations in the chained
|
||||
* actions. This one instead uses
|
||||
* {@link CompletableFuture#thenCompose(java.util.function.Function)} to schedule a final action
|
||||
* which performs the actual completion via the executor.
|
||||
*
|
||||
* @param <T> the type of the future value
|
||||
* @param cf the future
|
||||
* @return a future gated via the client executor
|
||||
*/
|
||||
default <T> CompletableFuture<T> gateFuture(CompletableFuture<T> cf) {
|
||||
return cf.thenCompose(this::gateFuture);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that dependent computations occur on the client executor
|
||||
*
|
||||
* <p>
|
||||
* Use as a method reference in a final call to
|
||||
* {@link CompletableFuture#thenCompose(java.util.function.Function)} to ensure the final stage
|
||||
* completes on the client executor.
|
||||
*
|
||||
* @param <T> the type of the future value
|
||||
* @param v the value
|
||||
* @return a future while completes with the given value on the client executor
|
||||
*/
|
||||
default <T> CompletableFuture<T> gateFuture(T v) {
|
||||
return CompletableFuture.supplyAsync(() -> v, getClientExecutor());
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -163,7 +163,7 @@ public class DefaultTargetObject<E extends TargetObject, P extends TargetObject>
|
||||
}
|
||||
req = curElemsRequest;
|
||||
}
|
||||
return req.thenApply(__ -> getCachedElements());
|
||||
return req.thenApply(__ -> getCachedElements()).thenCompose(model::gateFuture);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -337,7 +337,7 @@ public class DefaultTargetObject<E extends TargetObject, P extends TargetObject>
|
||||
}
|
||||
return getCachedAttributes();
|
||||
}
|
||||
});
|
||||
}).thenCompose(model::gateFuture);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+5
-14
@@ -20,11 +20,12 @@ import static org.junit.Assert.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import generic.Unique;
|
||||
import ghidra.async.AsyncTestUtils;
|
||||
import ghidra.async.AsyncUtils;
|
||||
import ghidra.dbg.DebuggerObjectModel;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
@@ -34,11 +35,8 @@ import ghidra.dbg.util.ElementsChangedListener.ElementsChangedInvocation;
|
||||
import ghidra.dbg.util.InvalidatedListener.InvalidatedInvocation;
|
||||
import ghidra.program.model.address.AddressFactory;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.util.SystemUtilities;
|
||||
|
||||
public class DefaultDebuggerObjectModelTest {
|
||||
static final long TIMEOUT_MILLISECONDS =
|
||||
SystemUtilities.isInTestingBatchMode() ? 5000 : Long.MAX_VALUE;
|
||||
public class DefaultDebuggerObjectModelTest implements AsyncTestUtils {
|
||||
|
||||
public static class FakeTargetObject extends DefaultTargetObject<TargetObject, TargetObject> {
|
||||
public FakeTargetObject(DebuggerObjectModel model, TargetObject parent, String name) {
|
||||
@@ -92,15 +90,6 @@ public class DefaultDebuggerObjectModelTest {
|
||||
}
|
||||
}
|
||||
|
||||
protected static <T> T waitOn(CompletableFuture<T> future) throws Throwable {
|
||||
try {
|
||||
return future.get(TIMEOUT_MILLISECONDS, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
catch (ExecutionException e) {
|
||||
throw e.getCause();
|
||||
}
|
||||
}
|
||||
|
||||
FakeDebuggerObjectModel model = new FakeDebuggerObjectModel();
|
||||
|
||||
@Test
|
||||
@@ -187,6 +176,7 @@ public class DefaultDebuggerObjectModelTest {
|
||||
|
||||
PhonyTargetObject phonyA = new PhonyTargetObject(model, model.root, "A");
|
||||
model.root.setAttributes(Map.of("A", phonyA), "Replace");
|
||||
waitOn(model.clientExecutor);
|
||||
|
||||
// Object-valued attribute replacement requires prior removal
|
||||
assertSame(fakeA, waitOn(model.fetchModelObject("A")));
|
||||
@@ -197,6 +187,7 @@ public class DefaultDebuggerObjectModelTest {
|
||||
// TODO: Should I permit custom equality check?
|
||||
model.root.setAttributes(Map.of(), "Clear");
|
||||
model.root.setAttributes(Map.of("A", phonyA), "Replace");
|
||||
waitOn(model.clientExecutor);
|
||||
|
||||
assertEquals(2, attrL.invocations.size());
|
||||
AttributesChangedInvocation changed = attrL.invocations.get(0);
|
||||
|
||||
+2
-2
@@ -57,7 +57,7 @@ public abstract class AbstractTestTargetRegisterBank<T extends AbstractTestTarge
|
||||
return regs.getModel().future(result).thenApply(__ -> {
|
||||
listeners.fire(TargetRegisterBankListener.class).registersUpdated(this, result);
|
||||
return result;
|
||||
});
|
||||
}).thenCompose(model::gateFuture);
|
||||
}
|
||||
|
||||
protected CompletableFuture<Void> writeRegs(Map<String, byte[]> values,
|
||||
@@ -81,7 +81,7 @@ public abstract class AbstractTestTargetRegisterBank<T extends AbstractTestTarge
|
||||
}
|
||||
future.thenAccept(__ -> {
|
||||
listeners.fire(TargetRegisterBankListener.class).registersUpdated(this, updates);
|
||||
});
|
||||
}).thenCompose(model::gateFuture);
|
||||
return future;
|
||||
}
|
||||
|
||||
|
||||
+2
-12
@@ -32,7 +32,7 @@ public class TestDebuggerObjectModel extends AbstractDebuggerObjectModel {
|
||||
CompletableFuture.delayedExecutor(DELAY_MILLIS, TimeUnit.MILLISECONDS);
|
||||
|
||||
enum FutureMode {
|
||||
SYNC, ASYNC, DELAYED;
|
||||
ASYNC, DELAYED;
|
||||
}
|
||||
|
||||
protected final AddressSpace ram =
|
||||
@@ -40,8 +40,6 @@ public class TestDebuggerObjectModel extends AbstractDebuggerObjectModel {
|
||||
protected final AddressFactory factory = new DefaultAddressFactory(new AddressSpace[] { ram });
|
||||
public final TestTargetSession session;
|
||||
|
||||
protected TestDebuggerObjectModel.FutureMode futureMode = FutureMode.SYNC;
|
||||
|
||||
protected int invalidateCachesCount;
|
||||
|
||||
public TestDebuggerObjectModel() {
|
||||
@@ -78,15 +76,7 @@ public class TestDebuggerObjectModel extends AbstractDebuggerObjectModel {
|
||||
}
|
||||
|
||||
public <T> CompletableFuture<T> future(T t) {
|
||||
switch (futureMode) {
|
||||
case SYNC:
|
||||
return CompletableFuture.completedFuture(t);
|
||||
case ASYNC:
|
||||
return CompletableFuture.supplyAsync(() -> t);
|
||||
case DELAYED:
|
||||
return CompletableFuture.supplyAsync(() -> t, DELAYED_EXECUTOR);
|
||||
}
|
||||
throw new AssertionError();
|
||||
return CompletableFuture.supplyAsync(() -> t, getClientExecutor());
|
||||
}
|
||||
|
||||
public CompletableFuture<Void> requestFocus(TargetObjectRef obj) {
|
||||
|
||||
+1
-1
@@ -64,7 +64,7 @@ public class TestTargetSession extends DefaultTargetModelRoot
|
||||
FOCUS_ATTRIBUTE_NAME, obj //
|
||||
), "Focus requested");
|
||||
listeners.fire(TargetFocusScopeListener.class).focusChanged(this, obj);
|
||||
});
|
||||
}).thenCompose(model::gateFuture);
|
||||
}
|
||||
|
||||
public void simulateStep(TestTargetThread eventThread) {
|
||||
|
||||
+12
@@ -17,12 +17,18 @@ package ghidra.pcode.exec;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
|
||||
public interface PcodeExecutorStatePiece<A, T> {
|
||||
|
||||
A longToOffset(AddressSpace space, long l);
|
||||
|
||||
default void setVar(Register reg, T val) {
|
||||
Address address = reg.getAddress();
|
||||
setVar(address.getAddressSpace(), address.getOffset(), reg.getMinimumByteSize(), true, val);
|
||||
}
|
||||
|
||||
default void setVar(Varnode var, T val) {
|
||||
Address address = var.getAddress();
|
||||
setVar(address.getAddressSpace(), address.getOffset(), var.getSize(), true, val);
|
||||
@@ -35,6 +41,12 @@ public interface PcodeExecutorStatePiece<A, T> {
|
||||
setVar(space, longToOffset(space, offset), size, truncateAddressableUnit, val);
|
||||
}
|
||||
|
||||
default T getVar(Register reg) {
|
||||
Address address = reg.getAddress();
|
||||
return getVar(address.getAddressSpace(), address.getOffset(), reg.getMinimumByteSize(),
|
||||
true);
|
||||
}
|
||||
|
||||
default T getVar(Varnode var) {
|
||||
Address address = var.getAddress();
|
||||
return getVar(address.getAddressSpace(), address.getOffset(), var.getSize(), true);
|
||||
|
||||
Reference in New Issue
Block a user