mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
GP-4209: GhidraTime-MSTTD integration. Type hints for (most) Python agents.
This commit is contained in:
parent
deb49d5322
commit
21a1602579
93 changed files with 6453 additions and 4118 deletions
|
@ -513,8 +513,9 @@ public class ObjectTableModel extends AbstractQueryTableModel<ValueRow> {
|
|||
}
|
||||
|
||||
protected Lifespan computeFullRange() {
|
||||
Long max = getTrace() == null ? null : getTrace().getTimeManager().getMaxSnap();
|
||||
return Lifespan.span(0L, max == null ? 1 : max + 1);
|
||||
Long maxBoxed = getTrace() == null ? null : getTrace().getTimeManager().getMaxSnap();
|
||||
long max = maxBoxed == null ? 0 : maxBoxed;
|
||||
return Lifespan.span(0L, max == Lifespan.DOMAIN.lmax() ? max : (max + 1));
|
||||
}
|
||||
|
||||
protected void updateTimelineMax() {
|
||||
|
|
|
@ -122,8 +122,9 @@ public class PathTableModel extends AbstractQueryTableModel<PathRow> {
|
|||
}
|
||||
|
||||
protected void updateTimelineMax() {
|
||||
Long max = getTrace() == null ? null : getTrace().getTimeManager().getMaxSnap();
|
||||
Lifespan fullRange = Lifespan.span(0L, max == null ? 1 : max + 1);
|
||||
Long maxBoxed = getTrace() == null ? null : getTrace().getTimeManager().getMaxSnap();
|
||||
long max = maxBoxed == null ? 0 : maxBoxed;
|
||||
Lifespan fullRange = Lifespan.span(0L, max == Lifespan.DOMAIN.lmax() ? max : (max + 1));
|
||||
lifespanPlotColumn.setFullRange(fullRange);
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
@ -17,8 +17,7 @@ package ghidra.app.plugin.core.debug.gui.time;
|
|||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Component;
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
import java.util.*;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -28,6 +27,7 @@ import javax.swing.table.*;
|
|||
|
||||
import docking.widgets.table.*;
|
||||
import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn;
|
||||
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
|
||||
import ghidra.docking.settings.Settings;
|
||||
import ghidra.framework.model.DomainObjectEvent;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
|
@ -112,9 +112,6 @@ public class DebuggerSnapshotTablePanel extends JPanel {
|
|||
}
|
||||
SnapshotRow row = new SnapshotRow(currentTrace, snapshot);
|
||||
snapshotTableModel.add(row);
|
||||
if (currentSnap == snapshot.getKey()) {
|
||||
snapshotFilterPanel.setSelectedItem(row);
|
||||
}
|
||||
}
|
||||
|
||||
private void snapChanged(TraceSnapshot snapshot) {
|
||||
|
@ -132,7 +129,7 @@ public class DebuggerSnapshotTablePanel extends JPanel {
|
|||
}
|
||||
}
|
||||
|
||||
final TableCellRenderer boldCurrentRenderer = new AbstractGColumnRenderer<Object>() {
|
||||
final TableCellRenderer styleCurrentRenderer = new AbstractGColumnRenderer<Object>() {
|
||||
@Override
|
||||
public String getFilterString(Object t, Settings settings) {
|
||||
return t == null ? "<null>" : t.toString();
|
||||
|
@ -142,9 +139,16 @@ public class DebuggerSnapshotTablePanel extends JPanel {
|
|||
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
|
||||
super.getTableCellRendererComponent(data);
|
||||
SnapshotRow row = (SnapshotRow) data.getRowObject();
|
||||
if (row != null && currentSnap != null && currentSnap.longValue() == row.getSnap()) {
|
||||
if (row == null || current == DebuggerCoordinates.NOWHERE) {
|
||||
// When used in a dialog, only currentTrace is set
|
||||
return this;
|
||||
}
|
||||
if (current.getViewSnap() == row.getSnap()) {
|
||||
setBold();
|
||||
}
|
||||
else if (current.getSnap() == row.getSnap()) {
|
||||
setItalic();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
@ -155,7 +159,7 @@ public class DebuggerSnapshotTablePanel extends JPanel {
|
|||
protected boolean hideScratch = true;
|
||||
|
||||
private Trace currentTrace;
|
||||
private volatile Long currentSnap;
|
||||
private volatile DebuggerCoordinates current = DebuggerCoordinates.NOWHERE;
|
||||
|
||||
protected final SnapshotListener listener = new SnapshotListener();
|
||||
|
||||
|
@ -173,19 +177,19 @@ public class DebuggerSnapshotTablePanel extends JPanel {
|
|||
TableColumnModel columnModel = snapshotTable.getColumnModel();
|
||||
TableColumn snapCol = columnModel.getColumn(SnapshotTableColumns.SNAP.ordinal());
|
||||
snapCol.setPreferredWidth(40);
|
||||
snapCol.setCellRenderer(boldCurrentRenderer);
|
||||
snapCol.setCellRenderer(styleCurrentRenderer);
|
||||
TableColumn timeCol = columnModel.getColumn(SnapshotTableColumns.TIMESTAMP.ordinal());
|
||||
timeCol.setPreferredWidth(200);
|
||||
timeCol.setCellRenderer(boldCurrentRenderer);
|
||||
timeCol.setCellRenderer(styleCurrentRenderer);
|
||||
TableColumn etCol = columnModel.getColumn(SnapshotTableColumns.EVENT_THREAD.ordinal());
|
||||
etCol.setPreferredWidth(40);
|
||||
etCol.setCellRenderer(boldCurrentRenderer);
|
||||
etCol.setCellRenderer(styleCurrentRenderer);
|
||||
TableColumn schdCol = columnModel.getColumn(SnapshotTableColumns.SCHEDULE.ordinal());
|
||||
schdCol.setPreferredWidth(60);
|
||||
schdCol.setCellRenderer(boldCurrentRenderer);
|
||||
schdCol.setCellRenderer(styleCurrentRenderer);
|
||||
TableColumn descCol = columnModel.getColumn(SnapshotTableColumns.DESCRIPTION.ordinal());
|
||||
descCol.setPreferredWidth(200);
|
||||
descCol.setCellRenderer(boldCurrentRenderer);
|
||||
descCol.setCellRenderer(styleCurrentRenderer);
|
||||
}
|
||||
|
||||
private void addNewListeners() {
|
||||
|
@ -235,14 +239,18 @@ public class DebuggerSnapshotTablePanel extends JPanel {
|
|||
return;
|
||||
}
|
||||
TraceTimeManager manager = currentTrace.getTimeManager();
|
||||
Collection<? extends TraceSnapshot> snapshots =
|
||||
hideScratch ? manager.getSnapshots(0, true, Long.MAX_VALUE, true)
|
||||
: manager.getAllSnapshots();
|
||||
// Use .collect instead of .toList to avoid size/sync issues
|
||||
// Even though access is synchronized, size may change during iteration
|
||||
snapshotTableModel.addAll(snapshots.stream()
|
||||
.map(s -> new SnapshotRow(currentTrace, s))
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
List<SnapshotRow> toAdd = new ArrayList<>();
|
||||
for (TraceSnapshot snapshot : hideScratch
|
||||
? manager.getSnapshots(0, true, Long.MAX_VALUE, true)
|
||||
: manager.getAllSnapshots()) {
|
||||
SnapshotRow row = new SnapshotRow(currentTrace, snapshot);
|
||||
toAdd.add(row);
|
||||
if (current != DebuggerCoordinates.NOWHERE &&
|
||||
snapshot.getKey() == current.getViewSnap()) {
|
||||
}
|
||||
}
|
||||
snapshotTableModel.addAll(toAdd);
|
||||
}
|
||||
|
||||
protected void deleteScratchSnapshots() {
|
||||
|
@ -270,9 +278,12 @@ public class DebuggerSnapshotTablePanel extends JPanel {
|
|||
return row == null ? null : row.getSnap();
|
||||
}
|
||||
|
||||
public void setCurrentSnapshot(Long snap) {
|
||||
currentSnap = snap;
|
||||
snapshotTableModel.fireTableDataChanged();
|
||||
public void setCurrent(DebuggerCoordinates coords) {
|
||||
boolean fire = coords.getViewSnap() != current.getViewSnap();
|
||||
current = coords;
|
||||
if (fire) {
|
||||
snapshotTableModel.fireTableDataChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public void setSelectedSnapshot(Long snap) {
|
||||
|
|
|
@ -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.
|
||||
|
@ -19,7 +19,6 @@ import static ghidra.app.plugin.core.debug.gui.DebuggerResources.*;
|
|||
|
||||
import java.awt.event.*;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JComponent;
|
||||
|
@ -37,6 +36,8 @@ import ghidra.framework.plugintool.*;
|
|||
import ghidra.framework.plugintool.AutoService.Wiring;
|
||||
import ghidra.framework.plugintool.annotation.AutoConfigStateField;
|
||||
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.time.TraceSnapshot;
|
||||
import ghidra.trace.model.time.schedule.TraceSchedule;
|
||||
import ghidra.util.HelpLocation;
|
||||
|
||||
|
@ -62,16 +63,6 @@ public class DebuggerTimeProvider extends ComponentProviderAdapter {
|
|||
}
|
||||
}
|
||||
|
||||
protected static boolean sameCoordinates(DebuggerCoordinates a, DebuggerCoordinates b) {
|
||||
if (!Objects.equals(a.getTrace(), b.getTrace())) {
|
||||
return false;
|
||||
}
|
||||
if (!Objects.equals(a.getTime(), b.getTime())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected final DebuggerTimePlugin plugin;
|
||||
|
||||
DebuggerCoordinates current = DebuggerCoordinates.NOWHERE;
|
||||
|
@ -154,7 +145,7 @@ public class DebuggerTimeProvider extends ComponentProviderAdapter {
|
|||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
if (e.getClickCount() == 2 && e.getButton() == MouseEvent.BUTTON1) {
|
||||
activateSelectedSnapshot();
|
||||
activateSelectedSnapshot(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -162,18 +153,44 @@ public class DebuggerTimeProvider extends ComponentProviderAdapter {
|
|||
@Override
|
||||
public void keyPressed(KeyEvent e) {
|
||||
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
|
||||
activateSelectedSnapshot();
|
||||
activateSelectedSnapshot(e);
|
||||
e.consume(); // lest it select the next row down
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void activateSelectedSnapshot() {
|
||||
Long snap = mainPanel.getSelectedSnapshot();
|
||||
if (snap != null && traceManager != null) {
|
||||
traceManager.activateSnap(snap);
|
||||
private TraceSchedule computeSelectedSchedule(InputEvent e, long snap) {
|
||||
if ((e.getModifiersEx() & InputEvent.SHIFT_DOWN_MASK) != 0) {
|
||||
return TraceSchedule.snap(snap);
|
||||
}
|
||||
if (snap >= 0) {
|
||||
return TraceSchedule.snap(snap);
|
||||
}
|
||||
Trace trace = current.getTrace();
|
||||
if (trace == null) {
|
||||
return TraceSchedule.snap(snap);
|
||||
}
|
||||
TraceSnapshot snapshot = trace.getTimeManager().getSnapshot(snap, false);
|
||||
if (snapshot == null) { // Really shouldn't happen, but okay
|
||||
return TraceSchedule.snap(snap);
|
||||
}
|
||||
TraceSchedule schedule = snapshot.getSchedule();
|
||||
if (schedule == null) {
|
||||
return TraceSchedule.snap(snap);
|
||||
}
|
||||
return schedule;
|
||||
}
|
||||
|
||||
private void activateSelectedSnapshot(InputEvent e) {
|
||||
if (traceManager == null) {
|
||||
return;
|
||||
}
|
||||
Long snap = mainPanel.getSelectedSnapshot();
|
||||
if (snap == null) {
|
||||
return;
|
||||
}
|
||||
traceManager.activateTime(computeSelectedSchedule(e, snap));
|
||||
}
|
||||
|
||||
protected void createActions() {
|
||||
|
@ -202,14 +219,9 @@ public class DebuggerTimeProvider extends ComponentProviderAdapter {
|
|||
}
|
||||
|
||||
public void coordinatesActivated(DebuggerCoordinates coordinates) {
|
||||
if (sameCoordinates(current, coordinates)) {
|
||||
current = coordinates;
|
||||
return;
|
||||
}
|
||||
current = coordinates;
|
||||
|
||||
mainPanel.setTrace(current.getTrace());
|
||||
mainPanel.setCurrentSnapshot(current.getSnap());
|
||||
mainPanel.setCurrent(current);
|
||||
}
|
||||
|
||||
public void writeConfigState(SaveState saveState) {
|
||||
|
|
|
@ -634,27 +634,6 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
|
|||
return task.future;
|
||||
}
|
||||
|
||||
protected TraceSnapshot findScratch(Trace trace, TraceSchedule time) {
|
||||
Collection<? extends TraceSnapshot> exist =
|
||||
trace.getTimeManager().getSnapshotsWithSchedule(time);
|
||||
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 = trace.getTimeManager().getMostRecentSnapshot(-1);
|
||||
long snap = last == null ? Long.MIN_VALUE : last.getKey() + 1;
|
||||
TraceSnapshot snapshot = trace.getTimeManager().getSnapshot(snap, true);
|
||||
snapshot.setDescription("Emulated");
|
||||
snapshot.setSchedule(time);
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
protected void installBreakpoints(Trace trace, long snap, DebuggerPcodeMachine<?> emu) {
|
||||
Lifespan span = Lifespan.at(snap);
|
||||
TraceBreakpointManager bm = trace.getBreakpointManager();
|
||||
|
@ -753,7 +732,8 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
|
|||
protected TraceSnapshot writeToScratch(CacheKey key, CachedEmulator ce) {
|
||||
TraceSnapshot destSnap;
|
||||
try (Transaction tx = key.trace.openTransaction("Emulate")) {
|
||||
destSnap = findScratch(key.trace, key.time);
|
||||
destSnap = key.trace.getTimeManager().findScratchSnapshot(key.time);
|
||||
destSnap.setDescription("Emulated");
|
||||
try {
|
||||
ce.emulator().writeDown(key.platform, destSnap.getKey(), key.time.getSnap());
|
||||
}
|
||||
|
|
|
@ -726,10 +726,42 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
|
|||
|
||||
@Override
|
||||
public CompletableFuture<Long> materialize(DebuggerCoordinates coordinates) {
|
||||
return materialize(DebuggerCoordinates.NOWHERE, coordinates, ActivationCause.USER);
|
||||
}
|
||||
|
||||
protected CompletableFuture<Long> materialize(DebuggerCoordinates previous,
|
||||
DebuggerCoordinates coordinates, ActivationCause cause) {
|
||||
/**
|
||||
* NOTE: If we're requested the snapshot, we don't care if we can find the snapshot already
|
||||
* materialized. We're going to let the back end actually materialize and activate. When it
|
||||
* activates (check the cause), we'll look for the materialized snapshot.
|
||||
*
|
||||
* If we go to a found snapshot on our request, the back-end may intermittently revert to
|
||||
* the another snapshot, because it may not realize what we've done at the front end, or it
|
||||
* may re-validate the request and go elsewhere, resulting in abrasive navigation. While we
|
||||
* could attempt some bookkeeping on the back-end, we don't control how the native debugger
|
||||
* issues events, so it's easier to just give it our request and then let it drive.
|
||||
*/
|
||||
ControlMode mode = getEffectiveControlMode(coordinates.getTrace());
|
||||
Target target = coordinates.getTarget();
|
||||
// NOTE: We've already validated at this point
|
||||
if (mode.isTarget() && cause == ActivationCause.USER && target != null) {
|
||||
// Yes, use getSnap for the materialized (view) snapshot
|
||||
return target.activateAsync(previous, coordinates).thenApply(__ -> target.getSnap());
|
||||
}
|
||||
|
||||
Long found = findSnapshot(coordinates);
|
||||
if (found != null) {
|
||||
return CompletableFuture.completedFuture(found);
|
||||
}
|
||||
|
||||
/**
|
||||
* NOTE: We can actually reach this point in RO_TARGET mode, though ordinarily, it should
|
||||
* only reach here in RW_EMULATOR mode. The reason is because during many automated tests,
|
||||
* the "default" mode of RO_TARGET is taken as the effective mode, and the tests still
|
||||
* expect emulation behavior. So do it.
|
||||
*/
|
||||
|
||||
if (emulationService == null) {
|
||||
Msg.warn(this, "Cannot navigate to coordinates with execution schedules, " +
|
||||
"because the emulation service is not available.");
|
||||
|
@ -738,16 +770,20 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
|
|||
return emulationService.backgroundEmulate(coordinates.getPlatform(), coordinates.getTime());
|
||||
}
|
||||
|
||||
protected CompletableFuture<Void> prepareViewAndFireEvent(DebuggerCoordinates coordinates,
|
||||
ActivationCause cause) {
|
||||
protected CompletableFuture<Void> prepareViewAndFireEvent(DebuggerCoordinates previous,
|
||||
DebuggerCoordinates coordinates, ActivationCause cause) {
|
||||
TraceVariableSnapProgramView varView = (TraceVariableSnapProgramView) coordinates.getView();
|
||||
if (varView == null) { // Should only happen with NOWHERE
|
||||
fireLocationEvent(coordinates, cause);
|
||||
return AsyncUtils.nil();
|
||||
}
|
||||
return materialize(coordinates).thenAcceptAsync(snap -> {
|
||||
return materialize(previous, coordinates, cause).thenAcceptAsync(snap -> {
|
||||
if (snap == null) {
|
||||
return; // The tool is probably closing
|
||||
/**
|
||||
* Either the tool is closing, or we're going to let the target materialize and
|
||||
* activate the actual snap.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
if (!coordinates.equals(current)) {
|
||||
return; // We navigated elsewhere before emulation completed
|
||||
|
@ -1150,21 +1186,12 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
|
|||
return AsyncUtils.nil();
|
||||
}
|
||||
CompletableFuture<Void> future =
|
||||
prepareViewAndFireEvent(resolved, cause).exceptionally(ex -> {
|
||||
prepareViewAndFireEvent(prev, resolved, cause).exceptionally(ex -> {
|
||||
// Emulation service will already display error
|
||||
doSetCurrent(prev);
|
||||
return null;
|
||||
});
|
||||
|
||||
if (cause != ActivationCause.USER) {
|
||||
return future;
|
||||
}
|
||||
Target target = resolved.getTarget();
|
||||
if (target == null) {
|
||||
return future;
|
||||
}
|
||||
|
||||
return future.thenCompose(__ -> target.activateAsync(prev, resolved));
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -24,6 +24,7 @@ import java.io.File;
|
|||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
|
@ -69,8 +70,7 @@ import ghidra.trace.database.ToyDBTraceBuilder;
|
|||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.target.schema.SchemaContext;
|
||||
import ghidra.trace.model.target.schema.XmlSchemaContext;
|
||||
import ghidra.util.InvalidNameException;
|
||||
import ghidra.util.NumericUtilities;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.datastruct.TestDataStructureErrorHandlerInstaller;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.ConsoleTaskMonitor;
|
||||
|
@ -310,6 +310,25 @@ public abstract class AbstractGhidraHeadedDebuggerTest
|
|||
}, () -> lastError.get().getMessage());
|
||||
}
|
||||
|
||||
public static void waitForPass(Object originator, Runnable runnable, long duration,
|
||||
TimeUnit unit) {
|
||||
long start = System.currentTimeMillis();
|
||||
while (System.currentTimeMillis() - start < unit.toMillis(duration)) {
|
||||
try {
|
||||
waitForPass(runnable);
|
||||
break;
|
||||
}
|
||||
catch (Throwable e) {
|
||||
Msg.warn(originator, "Long wait: " + e);
|
||||
try {
|
||||
Thread.sleep(500);
|
||||
}
|
||||
catch (InterruptedException e1) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T waitForPass(Supplier<T> supplier) {
|
||||
var locals = new Object() {
|
||||
AssertionError lastError;
|
||||
|
|
|
@ -33,8 +33,10 @@ import ghidra.trace.model.breakpoint.TraceBreakpoint;
|
|||
import ghidra.trace.model.breakpoint.TraceBreakpointKind;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
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.schedule.TraceSchedule.ScheduleForm;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
|
@ -70,6 +72,11 @@ public class MockTarget implements Target {
|
|||
return snap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScheduleForm getSupportedTimeForm(TraceObject obj, long snap) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, ActionEntry> collectActions(ActionName name, ActionContext context,
|
||||
ObjectArgumentPolicy policy) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue