GP-4144: Many fixes, esp., for dbgeng Trace RMI.

This commit is contained in:
Dan 2024-01-29 14:56:28 -05:00
parent 1281fb979b
commit a6549947ab
30 changed files with 1526 additions and 725 deletions

View file

@ -23,6 +23,7 @@ import ghidra.rmi.trace.TraceRmi.*;
import ghidra.trace.model.Trace;
import ghidra.trace.model.target.TraceObject;
import ghidra.trace.model.time.TraceSnapshot;
import ghidra.util.Msg;
class OpenTrace implements ValueDecoder {
final DoId doId;
@ -84,19 +85,47 @@ class OpenTrace implements ValueDecoder {
@Override
public Address toAddress(Addr addr, boolean required) {
/**
* Do not clamp here, like we do for ranges. The purpose of the given address is more
* specific here. Plus, we're not just omitting some addresses (like we would for ranges),
* we'd be moving the address. Thus, we'd be applying some attribute to a location that was
* never intended.
*/
AddressSpace space = getSpace(addr.getSpace(), required);
return space.getAddress(addr.getOffset());
}
@Override
public AddressRange toRange(AddrRange range, boolean required)
throws AddressOverflowException {
public AddressRange toRange(AddrRange range, boolean required) {
AddressSpace space = getSpace(range.getSpace(), required);
if (space == null) {
return null;
}
Address min = space.getAddress(range.getOffset());
Address max = space.getAddress(range.getOffset() + range.getExtend());
/**
* Clamp to only the valid addresses, but do at least warn.
*/
long minOffset = range.getOffset();
if (Long.compareUnsigned(minOffset, space.getMinAddress().getOffset()) < 0) {
Msg.warn(this, "Range [%s:%x+%x] partially exceeds space min. Clamping."
.formatted(range.getSpace(), range.getOffset(), range.getExtend()));
minOffset = space.getMinAddress().getOffset();
}
else if (Long.compareUnsigned(minOffset, space.getMaxAddress().getOffset()) > 0) {
throw new AddressOutOfBoundsException("Range [%s:%x+%x] entirely exceeds space max"
.formatted(range.getSpace(), range.getOffset(), range.getExtend()));
}
long maxOffset = range.getOffset() + range.getExtend(); // Use the requested offset, not adjusted
if (Long.compareUnsigned(maxOffset, space.getMaxAddress().getOffset()) > 0) {
Msg.warn(this, "Range [%s:%x+%x] partially exceeds space max. Clamping."
.formatted(range.getSpace(), range.getOffset(), range.getExtend()));
maxOffset = space.getMaxAddress().getOffset();
}
else if (Long.compareUnsigned(maxOffset, space.getMinAddress().getOffset()) < 0) {
throw new AddressOutOfBoundsException("Range [%s:%x+%x] entirely exceeds space min"
.formatted(range.getSpace(), range.getOffset(), range.getExtend()));
}
Address min = space.getAddress(minOffset);
Address max = space.getAddress(maxOffset);
return new AddressRangeImpl(min, max);
}

View file

@ -503,22 +503,29 @@ public class TraceRmiHandler implements TraceRmiConnection {
}
default String toString(RootMessage req) {
return switch (req.getMsgCase()) {
case REQUEST_ACTIVATE -> "activate(%d, %d, %s)".formatted(
req.getRequestActivate().getOid().getId(),
req.getRequestActivate().getObject().getId(),
req.getRequestActivate().getObject().getPath().getPath());
case REQUEST_END_TX -> "endTx(%d)".formatted(
req.getRequestEndTx().getTxid().getId());
case REQUEST_START_TX -> "startTx(%d,%s)".formatted(
req.getRequestStartTx().getTxid().getId(),
req.getRequestStartTx().getDescription());
case REQUEST_SET_VALUE -> "setValue(%d,%s,%s)".formatted(
req.getRequestSetValue().getValue().getParent().getId(),
req.getRequestSetValue().getValue().getParent().getPath().getPath(),
req.getRequestSetValue().getValue().getKey());
default -> null;
};
try {
return switch (req.getMsgCase()) {
case REQUEST_ACTIVATE -> "activate(%d, %d, %s)".formatted(
req.getRequestActivate().getOid().getId(),
req.getRequestActivate().getObject().getId(),
req.getRequestActivate().getObject().getPath().getPath());
case REQUEST_END_TX -> "endTx(%d)".formatted(
req.getRequestEndTx().getTxid().getId());
case REQUEST_START_TX -> "startTx(%d,%s)".formatted(
req.getRequestStartTx().getTxid().getId(),
req.getRequestStartTx().getDescription());
case REQUEST_SET_VALUE -> "setValue(%d,%s,%s,=%s)".formatted(
req.getRequestSetValue().getValue().getParent().getId(),
req.getRequestSetValue().getValue().getParent().getPath().getPath(),
req.getRequestSetValue().getValue().getKey(),
ValueDecoder.DISPLAY
.toValue(req.getRequestSetValue().getValue().getValue()));
default -> null;
};
}
catch (Throwable e) {
return "ERROR toStringing request: " + e;
}
}
}

View file

@ -70,7 +70,7 @@ public class TraceRmiTarget extends AbstractTarget {
private final Trace trace;
private final Matches matches = new Matches();
private final RequestCaches requestCaches = new RequestCaches();
private final RequestCaches requestCaches = new DorkedRequestCaches();
private final Set<TraceBreakpointKind> supportedBreakpointKinds;
public TraceRmiTarget(PluginTool tool, TraceRmiConnection connection, Trace trace) {
@ -760,21 +760,41 @@ public class TraceRmiTarget extends AbstractTarget {
}
}
protected static class RequestCaches {
interface RequestCaches {
void invalidate();
void invalidateMemory();
CompletableFuture<Void> readBlock(Address min, RemoteMethod method,
Map<String, Object> args);
CompletableFuture<Void> readRegs(TraceObject obj, RemoteMethod method,
Map<String, Object> args);
}
protected static class DefaultRequestCaches implements RequestCaches {
final Map<TraceObject, CompletableFuture<Void>> readRegs = new HashMap<>();
final Map<Address, CompletableFuture<Void>> readBlock = new HashMap<>();
@Override
public synchronized void invalidateMemory() {
readBlock.clear();
}
@Override
public synchronized void invalidate() {
readRegs.clear();
readBlock.clear();
}
@Override
public synchronized CompletableFuture<Void> readRegs(TraceObject obj, RemoteMethod method,
Map<String, Object> args) {
return readRegs.computeIfAbsent(obj,
o -> method.invokeAsync(args).toCompletableFuture().thenApply(__ -> null));
}
@Override
public synchronized CompletableFuture<Void> readBlock(Address min, RemoteMethod method,
Map<String, Object> args) {
return readBlock.computeIfAbsent(min,
@ -782,6 +802,28 @@ public class TraceRmiTarget extends AbstractTarget {
}
}
protected static class DorkedRequestCaches implements RequestCaches {
@Override
public void invalidate() {
}
@Override
public void invalidateMemory() {
}
@Override
public CompletableFuture<Void> readBlock(Address min, RemoteMethod method,
Map<String, Object> args) {
return method.invokeAsync(args).toCompletableFuture().thenApply(__ -> null);
}
@Override
public CompletableFuture<Void> readRegs(TraceObject obj, RemoteMethod method,
Map<String, Object> args) {
return method.invokeAsync(args).toCompletableFuture().thenApply(__ -> null);
}
}
@Override
public CompletableFuture<Void> activateAsync(DebuggerCoordinates prev,
DebuggerCoordinates coords) {
@ -818,6 +860,7 @@ public class TraceRmiTarget extends AbstractTarget {
@Override
public CompletableFuture<Void> invalidateMemoryCachesAsync() {
requestCaches.invalidateMemory();
return AsyncUtils.nil();
}

View file

@ -15,6 +15,9 @@
*/
package ghidra.app.plugin.core.debug.service.tracermi;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang3.ArrayUtils;
import ghidra.program.model.address.*;
@ -22,6 +25,44 @@ import ghidra.rmi.trace.TraceRmi.*;
public interface ValueDecoder {
ValueDecoder DEFAULT = new ValueDecoder() {};
ValueDecoder DISPLAY = new ValueDecoder() {
final Map<String, AddressSpace> spaces = new HashMap<>();
private AddressSpace getSpace(String space) {
return spaces.computeIfAbsent(space, name -> {
return new GenericAddressSpace(name, 64, AddressSpace.TYPE_RAM, 0);
});
}
@Override
public Address toAddress(Addr addr, boolean required) {
AddressSpace space = getSpace(addr.getSpace());
return space.getAddress(addr.getOffset());
}
@Override
public AddressRange toRange(AddrRange range, boolean required) {
AddressSpace space = getSpace(range.getSpace());
Address min = space.getAddress(range.getOffset());
Address max = space.getAddress(range.getOffset() + range.getExtend());
return new AddressRangeImpl(min, max);
}
@Override
public Object getObject(ObjDesc desc, boolean required) {
return "<Object id=%d path=%s>".formatted(desc.getId(), desc.getPath().getPath());
}
@Override
public Object getObject(ObjSpec spec, boolean required) {
return switch (spec.getKeyCase()) {
case KEY_NOT_SET -> "<ERROR: No key>";
case ID -> "<Object id=%d>".formatted(spec.getId());
case PATH -> "<Object path=%s>".formatted(spec.getPath());
default -> "<ERROR: default>";
};
}
};
default Address toAddress(Addr addr, boolean required) {
if (required) {
@ -30,8 +71,7 @@ public interface ValueDecoder {
return null;
}
default AddressRange toRange(AddrRange range, boolean required)
throws AddressOverflowException {
default AddressRange toRange(AddrRange range, boolean required) {
if (required) {
throw new IllegalStateException("AddressRange requires a trace for context");
}
@ -52,7 +92,7 @@ public interface ValueDecoder {
return null;
}
default Object toValue(Value value) throws AddressOverflowException {
default Object toValue(Value value) {
return switch (value.getValueCase()) {
case NULL_VALUE -> null;
case BOOL_VALUE -> value.getBoolValue();

View file

@ -61,7 +61,7 @@ class Receiver(Thread):
result = self.client._handle_invoke_method(request)
Client._write_value(
reply.xreply_invoke_method.return_value, result)
except Exception as e:
except BaseException as e:
reply.xreply_invoke_method.error = ''.join(
traceback.format_exc())
self.client._send(reply)
@ -79,7 +79,7 @@ class Receiver(Thread):
result = request.handler(
getattr(reply, request.field_name))
request.set_result(result)
except Exception as e:
except BaseException as e:
request.set_exception(e)
def _recv(self, field_name, handler):