GP-3872: Port scripting API to Trace RMI

This commit is contained in:
Dan 2024-03-25 15:20:38 -04:00
parent 77923fa693
commit ad6cb5892d
32 changed files with 3135 additions and 2063 deletions

View file

@ -146,6 +146,15 @@ public abstract class AbstractTraceRmiLaunchOffer implements TraceRmiLaunchOffer
this.terminalService = Objects.requireNonNull(tool.getService(TerminalService.class));
}
@Override
public boolean equals(Object obj) {
if (this.getClass() != obj.getClass()) {
return false;
}
AbstractTraceRmiLaunchOffer other = (AbstractTraceRmiLaunchOffer) obj;
return this.getConfigName().equals(other.getConfigName());
}
protected int getTimeoutMillis() {
return DEFAULT_TIMEOUT_MILLIS;
}

View file

@ -192,6 +192,15 @@ public class TraceRmiLauncherServicePlugin extends Plugin
.toList();
}
@Override
public List<TraceRmiLaunchOffer> getSavedOffers(Program program) {
Map<String, Long> savedConfigs = loadSavedConfigs(program);
return getOffers(program).stream()
.filter(o -> savedConfigs.containsKey(o.getConfigName()))
.sorted(Comparator.comparing(o -> -savedConfigs.get(o.getConfigName())))
.toList();
}
protected void executeTask(Task task) {
ProgressService progressService = tool.getService(ProgressService.class);
if (progressService != null) {

View file

@ -25,7 +25,6 @@ import org.apache.commons.lang3.exception.ExceptionUtils;
import docking.ActionContext;
import ghidra.app.context.ProgramLocationActionContext;
import ghidra.app.plugin.core.debug.gui.model.DebuggerObjectActionContext;
import ghidra.app.plugin.core.debug.gui.tracermi.RemoteMethodInvocationDialog;
import ghidra.app.plugin.core.debug.service.target.AbstractTarget;
import ghidra.app.services.DebuggerConsoleService;
@ -38,6 +37,7 @@ import ghidra.dbg.target.schema.TargetObjectSchema.SchemaName;
import ghidra.dbg.util.PathMatcher;
import ghidra.dbg.util.PathPredicates;
import ghidra.dbg.util.PathPredicates.Align;
import ghidra.debug.api.model.DebuggerObjectActionContext;
import ghidra.debug.api.target.ActionName;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.debug.api.tracermi.*;
@ -51,6 +51,7 @@ import ghidra.trace.model.Trace;
import ghidra.trace.model.breakpoint.*;
import ghidra.trace.model.breakpoint.TraceBreakpointKind.TraceBreakpointKindSet;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.memory.TraceMemoryRegion;
import ghidra.trace.model.memory.TraceObjectMemoryRegion;
import ghidra.trace.model.stack.*;
import ghidra.trace.model.target.*;
@ -611,6 +612,13 @@ public class TraceRmiTarget extends AbstractTarget {
}
}
record ExecuteMatcher(int score, List<ParamSpec> spec) implements MethodMatcher {
static final ExecuteMatcher HAS_CMD_TOSTRING = new ExecuteMatcher(2, List.of(
new TypeParamSpec("command", String.class),
new TypeParamSpec("toString", Boolean.class)));
static final List<ExecuteMatcher> ALL = matchers(HAS_CMD_TOSTRING);
}
record ReadMemMatcher(int score, List<ParamSpec> spec) implements MethodMatcher {
static final ReadMemMatcher HAS_PROC_RANGE = new ReadMemMatcher(2, List.of(
new TypeParamSpec("process", TargetProcess.class),
@ -841,6 +849,18 @@ public class TraceRmiTarget extends AbstractTarget {
}
}
@Override
public CompletableFuture<String> executeAsync(String command, boolean toString) {
MatchedMethod execute = matches.getBest("execute", ActionName.EXECUTE, ExecuteMatcher.ALL);
if (execute == null) {
return CompletableFuture.failedFuture(new NoSuchElementException());
}
Map<String, Object> args = new HashMap<>();
args.put(execute.params.get("command").name(), command);
args.put(execute.params.get("toString").name(), toString);
return execute.method.invokeAsync(args).toCompletableFuture().thenApply(v -> (String) v);
}
@Override
public CompletableFuture<Void> activateAsync(DebuggerCoordinates prev,
DebuggerCoordinates coords) {
@ -906,15 +926,11 @@ public class TraceRmiTarget extends AbstractTarget {
}
protected TraceObject getProcessForSpace(AddressSpace space) {
for (TraceObjectValue objVal : trace.getObjectManager()
.getValuesIntersecting(
for (TraceMemoryRegion region : trace.getMemoryManager()
.getRegionsIntersecting(
Lifespan.at(getSnap()),
new AddressRangeImpl(space.getMinAddress(), space.getMaxAddress()),
TargetMemoryRegion.RANGE_ATTRIBUTE_NAME)) {
TraceObject obj = objVal.getParent();
if (!obj.getInterfaces().contains(TraceObjectMemoryRegion.class)) {
continue;
}
new AddressRangeImpl(space.getMinAddress(), space.getMaxAddress()))) {
TraceObject obj = ((TraceObjectMemoryRegion) region).getObject();
return obj.queryCanonicalAncestorsTargetInterface(TargetProcess.class)
.findFirst()
.orElse(null);

View file

@ -20,12 +20,14 @@ import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import ghidra.app.services.DebuggerTargetService;
import ghidra.async.AsyncPairingQueue;
import ghidra.async.AsyncUtils;
import ghidra.dbg.target.schema.TargetObjectSchema;
import ghidra.dbg.target.schema.TargetObjectSchema.SchemaName;
import ghidra.debug.api.target.ActionName;
import ghidra.debug.api.target.Target;
@ -65,6 +67,19 @@ public class TestTraceRmiConnection implements TraceRmiConnection {
retType);
}
public TestRemoteMethod(String name, ActionName action, String display, String description,
Map<String, RemoteParameter> parameters, TargetObjectSchema retType) {
this(name, action, display, description, parameters, retType.getName(),
new AsyncPairingQueue<>(), new AsyncPairingQueue<>());
}
public TestRemoteMethod(String name, ActionName action, String display, String description,
TargetObjectSchema retType, RemoteParameter... parameters) {
this(name, action, display, description, Stream.of(parameters)
.collect(Collectors.toMap(RemoteParameter::name, p -> p)),
retType);
}
@Override
public RemoteAsyncResult invokeAsync(Map<String, Object> arguments) {
argQueue.give().complete(arguments);
@ -80,10 +95,24 @@ public class TestTraceRmiConnection implements TraceRmiConnection {
public void result(Object ret) {
retQueue.give().complete(ret);
}
public CompletableFuture<Map<String, Object>> expect(
Function<Map<String, Object>, Object> impl) {
record ArgsRet(Map<String, Object> args, Object ret) {
}
var result = argQueue().take().thenApply(a -> new ArgsRet(a, impl.apply(a)));
result.thenApply(ar -> ar.ret).handle(AsyncUtils.copyTo(retQueue().give()));
return result.thenApply(ar -> ar.args);
}
}
public record TestRemoteParameter(String name, SchemaName type, boolean required,
Object defaultValue, String display, String description) implements RemoteParameter {
public TestRemoteParameter(String name, TargetObjectSchema type, boolean required,
Object defaultValue, String display, String description) {
this(name, type.getName(), required, defaultValue, display, description);
}
@Override
public Object getDefaultValue() {
return defaultValue;