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

@ -201,6 +201,9 @@ public class DBTraceTimeViewport implements TraceTimeViewport {
while (true) {
TraceSnapshot fork = locateMostRecentFork(timeManager, curSnap);
long prevSnap = fork == null ? Long.MIN_VALUE : fork.getKey();
if (curSnap >= 0 && prevSnap < 0) {
prevSnap = 0;
}
if (!addSnapRange(prevSnap, curSnap, spanSet, ordered)) {
return;
}

View file

@ -585,6 +585,9 @@ public class DBTraceMemorySpace
protected void doPutFutureBytes(OffsetSnap loc, ByteBuffer buf, int dstOffset, int maxLen,
OutSnap lastSnap, Set<TraceAddressSnapRange> changed) throws IOException {
if (loc.snap == Lifespan.DOMAIN.lmax()) {
return;
}
// NOTE: Do not leave the buffer advanced from here
int pos = buf.position();
// exclusive?
@ -616,7 +619,7 @@ public class DBTraceMemorySpace
}
}
if (!remaining.isEmpty()) {
lastSnap.snap = Long.MAX_VALUE;
lastSnap.snap = Lifespan.DOMAIN.lmax();
for (AddressRange rng : remaining) {
changed.add(
new ImmutableTraceAddressSnapRange(rng, Lifespan.nowOnMaybeScratch(loc.snap)));

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -133,6 +133,26 @@ public class DBTraceTimeManager implements TraceTimeManager, DBTraceManager {
return snapshotsBySchedule.get(schedule.toString());
}
@Override
public TraceSnapshot findScratchSnapshot(TraceSchedule schedule) {
Collection<? extends TraceSnapshot> exist = getSnapshotsWithSchedule(schedule);
if (!exist.isEmpty()) {
return exist.iterator().next();
}
/**
* TODO: This could be more sophisticated.... Does it need to be, though? Ideally, we'd only
* keep state around that has annotations, e.g., bookmarks and code units. That needs a new
* query (latestStartSince) on those managers, though. It must find the latest start tick
* since a given snap. We consider only start snaps because placed code units go "from now
* on out".
*/
TraceSnapshot last = getMostRecentSnapshot(-1);
long snap = last == null ? Long.MIN_VALUE : last.getKey() + 1;
TraceSnapshot snapshot = getSnapshot(snap, true);
snapshot.setSchedule(schedule);
return snapshot;
}
@Override
public Collection<? extends DBTraceSnapshot> getAllSnapshots() {
return Collections.unmodifiableCollection(snapshotStore.asMap().values());

View file

@ -806,6 +806,6 @@ public interface TraceObject extends TraceUniqueObject {
if (stateVal == null) {
return TraceExecutionState.INACTIVE;
}
return TraceExecutionState.valueOf((String) stateVal.getValue());
return TraceExecutionState.valueOf(stateVal.castValue());
}
}

View file

@ -16,6 +16,7 @@
package ghidra.trace.model.target.iface;
import ghidra.trace.model.target.info.TraceObjectInfo;
import ghidra.trace.model.time.schedule.TraceSchedule.ScheduleForm;
/**
* An object that can emit events affecting itself and its successors
@ -28,8 +29,11 @@ import ghidra.trace.model.target.info.TraceObjectInfo;
shortName = "event scope",
attributes = {
TraceObjectEventScope.KEY_EVENT_THREAD,
TraceObjectEventScope.KEY_TIME_SUPPORT,
},
fixedKeys = {})
public interface TraceObjectEventScope extends TraceObjectInterface {
String KEY_EVENT_THREAD = "_event_thread";
/** See {@link ScheduleForm} */
String KEY_TIME_SUPPORT = "_time_support";
}

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -52,10 +52,25 @@ public interface TraceTimeManager {
* at most one snapshot.
*
* @param schedule the schedule to find
* @return the snapshot, or {@code null} if no such snapshot exists
* @return the snapshots
*/
Collection<? extends TraceSnapshot> getSnapshotsWithSchedule(TraceSchedule schedule);
/**
* Find or create a the snapshot with the given schedule
*
* <p>
* If a snapshot with the given schedule already exists, this returns the first such snapshot
* found. Ideally, there is exactly one. If this method is consistently used for creating
* scratch snapshots, then that should always be the case. If no such snapshot exists, this
* creates a snapshot with the minimum available negative snapshot key, that is starting at
* {@link Long#MIN_VALUE} and increasing from there.
*
* @param schedule the schedule to find
* @return the snapshot
*/
TraceSnapshot findScratchSnapshot(TraceSchedule schedule);
/**
* List all snapshots in the trace
*

View file

@ -30,8 +30,153 @@ import ghidra.util.task.TaskMonitor;
* A sequence of emulator stepping commands, essentially comprising a "point in time."
*/
public class TraceSchedule implements Comparable<TraceSchedule> {
/**
* The initial snapshot (with no steps)
*/
public static final TraceSchedule ZERO = TraceSchedule.snap(0);
/**
* Specifies forms of a stepping schedule.
*
* <p>
* Each form defines a set of stepping schedules. It happens that each is a subset of the next.
* A {@link #SNAP_ONLY} schedule is also a {@link #SNAP_ANY_STEPS_OPS} schedule, but not
* necessarily vice versa.
*/
public enum ScheduleForm {
/**
* The schedule consists only of a snapshot. No stepping after.
*/
SNAP_ONLY {
@Override
public boolean contains(Trace trace, TraceSchedule schedule) {
return schedule.steps.isNop() && schedule.pSteps.isNop();
}
},
/**
* The schedule consists of a snapshot and some number of instruction steps on the event
* thread only.
*/
SNAP_EVT_STEPS {
@Override
public boolean contains(Trace trace, TraceSchedule schedule) {
if (!schedule.pSteps.isNop()) {
return false;
}
List<Step> steps = schedule.steps.getSteps();
if (steps.isEmpty()) {
return true;
}
if (steps.size() != 1) {
return false;
}
if (!(steps.getFirst() instanceof TickStep ticks)) {
return false;
}
if (ticks.getThreadKey() == -1) {
return true;
}
if (trace == null) {
return false;
}
TraceThread eventThread = schedule.getEventThread(trace);
TraceThread thread = ticks.getThread(trace.getThreadManager(), eventThread);
if (eventThread != thread) {
return false;
}
return true;
}
@Override
public TraceSchedule validate(Trace trace, TraceSchedule schedule) {
if (!schedule.pSteps.isNop()) {
return null;
}
List<Step> steps = schedule.steps.getSteps();
if (steps.isEmpty()) {
return schedule;
}
if (steps.size() != 1) {
return null;
}
if (!(steps.getFirst() instanceof TickStep ticks)) {
return null;
}
if (ticks.getThreadKey() == -1) {
return schedule;
}
if (trace == null) {
return null;
}
TraceThread eventThread = schedule.getEventThread(trace);
TraceThread thread = ticks.getThread(trace.getThreadManager(), eventThread);
if (eventThread != thread) {
return null;
}
return TraceSchedule.snap(schedule.snap).steppedForward(null, ticks.getTickCount());
}
},
/**
* The schedule consists of a snapshot and a sequence of instruction steps on any
* threads(s).
*/
SNAP_ANY_STEPS {
@Override
public boolean contains(Trace trace, TraceSchedule schedule) {
return schedule.pSteps.isNop();
}
},
/**
* The schedule consists of a snapshot and a sequence of instruction steps then p-code op
* steps on any thread(s).
*
* <p>
* This is the most capable form supported by {@link TraceSchedule}.
*/
SNAP_ANY_STEPS_OPS {
@Override
public boolean contains(Trace trace, TraceSchedule schedule) {
return true;
}
};
public static final List<ScheduleForm> VALUES = List.of(ScheduleForm.values());
/**
* Check if the given schedule conforms
*
* @param trace if available, a trace for determining the event thread
* @param schedule the schedule to test
* @return true if the schedule adheres to this form
*/
public abstract boolean contains(Trace trace, TraceSchedule schedule);
/**
* If the given schedule conforms, normalize the schedule to prove it does.
*
* @param trace if available, a trace for determining the event thread
* @param schedule the schedule to test
* @return the non-null normalized schedule, or null if the given schedule does not conform
*/
public TraceSchedule validate(Trace trace, TraceSchedule schedule) {
if (!contains(trace, schedule)) {
return null;
}
return schedule;
}
/**
* Get the more restrictive of this and the given form
*
* @param that the other form
* @return the more restrictive form
*/
public ScheduleForm intersect(ScheduleForm that) {
int ord = Math.min(this.ordinal(), that.ordinal());
return VALUES.get(ord);
}
}
/**
* Create a schedule that consists solely of a snapshot
*
@ -256,7 +401,7 @@ public class TraceSchedule implements Comparable<TraceSchedule> {
* loading a snapshot
*/
public boolean isSnapOnly() {
return steps.isNop() && pSteps.isNop();
return ScheduleForm.SNAP_ONLY.contains(null, this);
}
/**