GP-4209: GhidraTime-MSTTD integration. Type hints for (most) Python agents.

This commit is contained in:
Dan 2025-03-24 18:28:07 +00:00
parent deb49d5322
commit 21a1602579
93 changed files with 6453 additions and 4118 deletions

View file

@ -43,6 +43,7 @@ import ghidra.trace.model.program.TraceVariableSnapProgramView;
import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.model.time.schedule.PatchStep;
import ghidra.trace.model.time.schedule.TraceSchedule;
import ghidra.trace.model.time.schedule.TraceSchedule.ScheduleForm;
import ghidra.trace.util.TraceRegisterUtils;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
@ -389,22 +390,47 @@ public enum ControlMode {
*/
public DebuggerCoordinates validateCoordinates(PluginTool tool,
DebuggerCoordinates coordinates, ActivationCause cause) {
if (!followsPresent()) {
if (!followsPresent() || cause != ActivationCause.USER) {
return coordinates;
}
Target target = coordinates.getTarget();
if (target == null) {
return coordinates;
}
if (cause == ActivationCause.USER &&
(!coordinates.getTime().isSnapOnly() || coordinates.getSnap() != target.getSnap())) {
tool.setStatusInfo(
"Cannot navigate time in %s mode. Switch to Trace or Emulate mode first."
ScheduleForm form =
target.getSupportedTimeForm(coordinates.getObject(), coordinates.getSnap());
if (form == null) {
if (coordinates.getTime().isSnapOnly() &&
coordinates.getSnap() == target.getSnap()) {
return coordinates;
}
else {
tool.setStatusInfo("""
Cannot navigate time in %s mode. Switch to Trace or Emulate mode first."""
.formatted(name),
true);
return null;
true);
return null;
}
}
return coordinates;
TraceSchedule norm = form.validate(coordinates.getTrace(), coordinates.getTime());
if (norm != null) {
return coordinates.time(norm);
}
String errMsg = switch (form) {
case SNAP_ONLY -> """
Target can only navigate to snapshots. Switch to Emulate mode first.""";
case SNAP_EVT_STEPS -> """
Target can only replay steps on the event thread. Switch to Emulate mode \
first.""";
case SNAP_ANY_STEPS -> """
Target cannot perform p-code steps. Switch to Emulate mode first.""";
case SNAP_ANY_STEPS_OPS -> throw new AssertionError();
};
tool.setStatusInfo(errMsg, true);
return null;
}
protected TracePlatform platformFor(DebuggerCoordinates coordinates, Address address) {

View file

@ -34,8 +34,12 @@ import ghidra.trace.model.breakpoint.TraceBreakpointKind;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.memory.TraceMemoryState;
import ghidra.trace.model.stack.TraceStackFrame;
import ghidra.trace.model.target.TraceObject;
import ghidra.trace.model.target.path.KeyPath;
import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.model.time.TraceSnapshot;
import ghidra.trace.model.time.schedule.TraceSchedule;
import ghidra.trace.model.time.schedule.TraceSchedule.ScheduleForm;
import ghidra.util.Swing;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
@ -278,13 +282,51 @@ public interface Target {
* Get the current snapshot key for the target
*
* <p>
* For most targets, this is the most recently created snapshot.
* For most targets, this is the most recently created snapshot. For time-traveling targets, if
* may not be. If this returns a negative number, then it refers to a scratch snapshot and
* almost certainly indicates time travel with instruction steps. Use {@link #getTime()} in that
* case to get a more precise schedule.
*
* @return the snapshot
*/
// TODO: Should this be TraceSchedule getTime()?
long getSnap();
/**
* Get the current time
*
* @return the current time
*/
default TraceSchedule getTime() {
long snap = getSnap();
if (snap >= 0) {
return TraceSchedule.snap(snap);
}
TraceSnapshot snapshot = getTrace().getTimeManager().getSnapshot(snap, false);
if (snapshot == null) {
return null;
}
return snapshot.getSchedule();
}
/**
* Get the form of schedules supported by "activate" on the back end
*
* <p>
* A non-null return value indicates the back end supports time travel. If it does, the return
* value indicates the form of schedules that can be activated, (i.e., via some "go to time"
* command). NOTE: Switching threads is considered an event by every time-traveling back end
* that we know of. Events are usually mapped to a Ghidra trace's snapshots, and so most back
* ends are constrained to schedules of the form {@link ScheduleForm#SNAP_EVT_STEPS}. A back-end
* based on emulation may support thread switching. To support p-code op stepping, the back-end
* will certainly have to be based on p-code emulation, and it must be using the same Sleigh
* language as Ghidra.
*
* @param obj the object (or an ancestor) that may support time travel
* @param snap the <em>destination</em> snapshot
* @return the form
*/
public ScheduleForm getSupportedTimeForm(TraceObject obj, long snap);
/**
* Collect all actions that implement the given common debugger command
*

View file

@ -402,7 +402,16 @@ public class DebuggerCoordinates {
return new DebuggerCoordinates(trace, platform, target, thread, view, newTime, frame, path);
}
/**
* Get these same coordinates with time replaced by the given schedule
*
* @param newTime the new schedule
* @return the new coordinates
*/
public DebuggerCoordinates time(TraceSchedule newTime) {
if (Objects.equals(time, newTime)) {
return this;
}
if (trace == null) {
return NOWHERE;
}