diff --git a/Ghidra/Debug/Debugger/certification.manifest b/Ghidra/Debug/Debugger/certification.manifest
index 2e722617b4..adbc4a2a23 100644
--- a/Ghidra/Debug/Debugger/certification.manifest
+++ b/Ghidra/Debug/Debugger/certification.manifest
@@ -120,6 +120,7 @@ src/main/help/help/topics/DebuggerTargetsPlugin/images/disconnect.png||GHIDRA|||
src/main/help/help/topics/DebuggerThreadsPlugin/DebuggerThreadsPlugin.html||GHIDRA||||END|
src/main/help/help/topics/DebuggerThreadsPlugin/images/DebuggerThreadsPlugin.png||GHIDRA||||END|
src/main/help/help/topics/DebuggerThreadsPlugin/images/continue.png||GHIDRA||||END|
+src/main/help/help/topics/DebuggerThreadsPlugin/images/skipover.png||GHIDRA||||END|
src/main/help/help/topics/DebuggerThreadsPlugin/images/stepback.png||GHIDRA||||END|
src/main/help/help/topics/DebuggerThreadsPlugin/images/stepinto.png||GHIDRA||||END|
src/main/help/help/topics/DebuggerTimePlugin/DebuggerTimePlugin.html||GHIDRA||||END|
diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerThreadsPlugin/DebuggerThreadsPlugin.html b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerThreadsPlugin/DebuggerThreadsPlugin.html
index 4c2cfc02a2..abff6b68fa 100644
--- a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerThreadsPlugin/DebuggerThreadsPlugin.html
+++ b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerThreadsPlugin/DebuggerThreadsPlugin.html
@@ -125,6 +125,18 @@
the next tick, using emulation. Note that emulation does not affect the target. Furthermore,
emulation may halt early if it encounters certain instructions or causes an exception.
+
Emulate
+ Trace Skip Tick Forward
+
+ This action is available when a thread is selected. It steps the current thread forward by
+ skipping the next instruction, using emulation. Note that emulation does not affect the target.
+ Furthermore, emulation may halt early if it encounters certain instructions or causes an
+ exception. This action may be used skip subroutines; however, the stack may require
+ additional patching, e.g., to clean up stack parameters, depending on the calling convention.
+ This action does not perform those patches automatically. It only advances the program
+ counter. You may use Go To Time to append the require stack patch,
+ e.g., t0-{RSP=RSP+8}
.
+
Seek Trace to
Present
diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerThreadsPlugin/images/DebuggerThreadsPlugin.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerThreadsPlugin/images/DebuggerThreadsPlugin.png
index 0b6569e3c2..3a10d754a3 100644
Binary files a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerThreadsPlugin/images/DebuggerThreadsPlugin.png and b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerThreadsPlugin/images/DebuggerThreadsPlugin.png differ
diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerThreadsPlugin/images/skipover.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerThreadsPlugin/images/skipover.png
new file mode 100644
index 0000000000..a477c46575
Binary files /dev/null and b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerThreadsPlugin/images/skipover.png differ
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerResources.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerResources.java
index 1aaf6e54f1..5f834dc155 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerResources.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerResources.java
@@ -87,6 +87,7 @@ public interface DebuggerResources {
ImageIcon ICON_STEP_INTO = ResourceManager.loadImage("images/stepinto.png");
ImageIcon ICON_STEP_OVER = ResourceManager.loadImage("images/stepover.png");
+ ImageIcon ICON_SKIP_OVER = ResourceManager.loadImage("images/skipover.png");
ImageIcon ICON_STEP_FINISH = ResourceManager.loadImage("images/stepout.png");
ImageIcon ICON_STEP_BACK = ResourceManager.loadImage("images/stepback.png");
// TODO: Draw new icons?
@@ -1641,81 +1642,12 @@ public interface DebuggerResources {
}
}
- interface StepSnapForwardAction {
- String NAME = "Step Trace Snap Forward";
- String DESCRIPTION = "Navigate the recording forward one snap";
- Icon ICON = ICON_SNAP_FORWARD;
- String GROUP = GROUP_CONTROL;
- String HELP_ANCHOR = "step_trace_snap_forward";
-
- static ActionBuilder builder(Plugin owner) {
- String ownerName = owner.getName();
- return new ActionBuilder(NAME, ownerName)
- .description(DESCRIPTION)
- .toolBarIcon(ICON)
- .toolBarGroup(GROUP, "4")
- .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
- }
- }
-
- abstract class AbstractStepSnapForwardAction extends DockingAction {
- public static final String NAME = StepSnapForwardAction.NAME;
- public static final Icon ICON = StepSnapForwardAction.ICON;
- public static final String HELP_ANCHOR = StepSnapForwardAction.HELP_ANCHOR;
-
- public AbstractStepSnapForwardAction(Plugin owner) {
- super(NAME, owner.getName());
- setDescription(StepSnapForwardAction.DESCRIPTION);
- setHelpLocation(new HelpLocation(owner.getName(), HELP_ANCHOR));
- }
- }
-
- abstract class AbstractEmulateTickForwardAction extends DockingAction {
- public static final String NAME = "Emulate Trace Tick Forward";
- public static final Icon ICON = ICON_STEP_INTO;
- public static final String HELP_ANCHOR = "emu_trace_tick_forward";
-
- public AbstractEmulateTickForwardAction(Plugin owner) {
- super(NAME, owner.getName());
- setDescription("Emulate the recording forward one tick");
- setHelpLocation(new HelpLocation(owner.getName(), HELP_ANCHOR));
- }
- }
-
- interface EmulatePcodeForwardAction {
- String NAME = "Emulate Trace p-code Forward";
- String DESCRIPTION = "Navigate the recording forward one p-code tick";
- Icon ICON = ICON_STEP_INTO;
- String GROUP = GROUP_CONTROL;
- String HELP_ANCHOR = "emu_trace_pcode_forward";
-
- static ActionBuilder builder(Plugin owner) {
- String ownerName = owner.getName();
- return new ActionBuilder(NAME, ownerName)
- .description(DESCRIPTION)
- .toolBarIcon(ICON)
- .toolBarGroup(GROUP)
- .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
- }
- }
-
- abstract class AbstractEmulateTickBackwardAction extends DockingAction {
- public static final String NAME = "Emulate Trace Tick Backward";
- public static final Icon ICON = ICON_STEP_BACK;
- public static final String HELP_ANCHOR = "emu_trace_tick_backward";
-
- public AbstractEmulateTickBackwardAction(Plugin owner) {
- super(NAME, owner.getName());
- setDescription("Emulate the recording backward one tick");
- setHelpLocation(new HelpLocation(owner.getName(), HELP_ANCHOR));
- }
- }
-
interface StepSnapBackwardAction {
String NAME = "Step Trace Snap Backward";
String DESCRIPTION = "Navigate the recording backward one snap";
Icon ICON = ICON_SNAP_BACKWARD;
String GROUP = GROUP_CONTROL;
+ String ORDER = "1";
String HELP_ANCHOR = "step_trace_snap_backward";
static ActionBuilder builder(Plugin owner) {
@@ -1723,20 +1655,80 @@ public interface DebuggerResources {
return new ActionBuilder(NAME, ownerName)
.description(DESCRIPTION)
.toolBarIcon(ICON)
- .toolBarGroup(GROUP, "1")
+ .toolBarGroup(GROUP, ORDER)
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
}
}
- abstract class AbstractStepSnapBackwardAction extends DockingAction {
- public static final String NAME = StepSnapBackwardAction.NAME;
- public static final Icon ICON = StepSnapBackwardAction.ICON;;
- public static final String HELP_ANCHOR = StepSnapBackwardAction.HELP_ANCHOR;
+ interface StepSnapForwardAction {
+ String NAME = "Step Trace Snap Forward";
+ String DESCRIPTION = "Navigate the recording forward one snap";
+ Icon ICON = ICON_SNAP_FORWARD;
+ String GROUP = GROUP_CONTROL;
+ String ORDER = "5";
+ String HELP_ANCHOR = "step_trace_snap_forward";
- public AbstractStepSnapBackwardAction(Plugin owner) {
- super(NAME, owner.getName());
- setDescription(StepSnapBackwardAction.DESCRIPTION);
- setHelpLocation(new HelpLocation(owner.getName(), HELP_ANCHOR));
+ static ActionBuilder builder(Plugin owner) {
+ String ownerName = owner.getName();
+ return new ActionBuilder(NAME, ownerName)
+ .description(DESCRIPTION)
+ .toolBarIcon(ICON)
+ .toolBarGroup(GROUP, ORDER)
+ .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
+ }
+ }
+
+ interface EmulateTickBackwardAction {
+ String NAME = "Emulate Trace Tick Backward";
+ String DESCRIPTION = "Emulate the recording backward one tick";
+ Icon ICON = ICON_STEP_BACK;
+ String GROUP = GROUP_CONTROL;
+ String ORDER = "2";
+ String HELP_ANCHOR = "emu_trace_tick_backward";
+
+ static ActionBuilder builder(Plugin owner) {
+ String ownerName = owner.getName();
+ return new ActionBuilder(NAME, ownerName)
+ .description(DESCRIPTION)
+ .toolBarIcon(ICON)
+ .toolBarGroup(GROUP, ORDER)
+ .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
+ }
+ }
+
+ interface EmulateTickForwardAction {
+ String NAME = "Emulate Trace Tick Forward";
+ String DESCRIPTION = "Emulate the recording forward one instruction";
+ Icon ICON = ICON_STEP_INTO;
+ String GROUP = GROUP_CONTROL;
+ String ORDER = "3";
+ String HELP_ANCHOR = "emu_trace_tick_forward";
+
+ static ActionBuilder builder(Plugin owner) {
+ String ownerName = owner.getName();
+ return new ActionBuilder(NAME, ownerName)
+ .description(DESCRIPTION)
+ .toolBarIcon(ICON)
+ .toolBarGroup(GROUP, ORDER)
+ .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
+ }
+ }
+
+ interface EmulateSkipTickForwardAction {
+ String NAME = "Emulate Trace Skip Tick Forward";
+ String DESCRIPTION = "Emulate the recording forward by skipping one instruction";
+ Icon ICON = ICON_SKIP_OVER;
+ String GROUP = GROUP_CONTROL;
+ String ORDER = "4";
+ String HELP_ANCHOR = "emu_trace_skip_tick_forward";
+
+ static ActionBuilder builder(Plugin owner) {
+ String ownerName = owner.getName();
+ return new ActionBuilder(NAME, ownerName)
+ .description(DESCRIPTION)
+ .toolBarIcon(ICON)
+ .toolBarGroup(GROUP, ORDER)
+ .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
}
}
@@ -1745,8 +1737,45 @@ public interface DebuggerResources {
String DESCRIPTION = "Navigate the recording backward one p-code tick";
Icon ICON = ICON_STEP_BACK;
String GROUP = GROUP_CONTROL;
+ String ORDER = "2";
String HELP_ANCHOR = "emu_trace_pcode_backward";
+ static ActionBuilder builder(Plugin owner) {
+ String ownerName = owner.getName();
+ return new ActionBuilder(NAME, ownerName)
+ .description(DESCRIPTION)
+ .toolBarIcon(ICON)
+ .toolBarGroup(GROUP, ORDER)
+ .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
+ }
+ }
+
+ interface EmulatePcodeForwardAction {
+ String NAME = "Emulate Trace p-code Forward";
+ String DESCRIPTION = "Emulate the recording forward one p-code tick";
+ Icon ICON = ICON_STEP_INTO;
+ String GROUP = GROUP_CONTROL;
+ String ORDER = "3";
+ String HELP_ANCHOR = "emu_trace_pcode_forward";
+
+ static ActionBuilder builder(Plugin owner) {
+ String ownerName = owner.getName();
+ return new ActionBuilder(NAME, ownerName)
+ .description(DESCRIPTION)
+ .toolBarIcon(ICON)
+ .toolBarGroup(GROUP, ORDER)
+ .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
+ }
+ }
+
+ interface EmulateSkipPcodeForwardAction {
+ String NAME = "Emulate Trace Skip P-code Forward";
+ String DESCRIPTION = "Emulate the recording forward by skipping one p-code op";
+ Icon ICON = ICON_SKIP_OVER;
+ String GROUP = GROUP_CONTROL;
+ String ORDER = "4";
+ String HELP_ANCHOR = "emu_trace_skip_pcode_forward";
+
static ActionBuilder builder(Plugin owner) {
String ownerName = owner.getName();
return new ActionBuilder(NAME, ownerName)
@@ -1757,15 +1786,20 @@ public interface DebuggerResources {
}
}
- abstract class AbstractSeekTracePresentAction extends ToggleDockingAction {
- public static final String NAME = "Seek Trace Present";
- public static final Icon ICON = ICON_SEEK_PRESENT;
- public static final String HELP_ANCHOR = "seek_trace_present";
+ interface SeekTracePresentAction {
+ String NAME = "Seek Trace Present";
+ String DESCRIPTION = "Track the tool to the latest snap";
+ Icon ICON = ICON_SEEK_PRESENT;
+ String GROUP = "zz";
+ String HELP_ANCHOR = "seek_trace_present";
- public AbstractSeekTracePresentAction(Plugin owner) {
- super(NAME, owner.getName());
- setDescription("Track the tool to the latest snap");
- setHelpLocation(new HelpLocation(owner.getName(), HELP_ANCHOR));
+ static ToggleActionBuilder builder(Plugin owner) {
+ String ownerName = owner.getName();
+ return new ToggleActionBuilder(NAME, ownerName)
+ .description(DESCRIPTION)
+ .toolBarIcon(ICON)
+ .toolBarGroup(GROUP)
+ .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
}
}
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/thread/DebuggerThreadsProvider.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/thread/DebuggerThreadsProvider.java
index 4c9cb5fd0f..bc81d21a9d 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/thread/DebuggerThreadsProvider.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/thread/DebuggerThreadsProvider.java
@@ -87,172 +87,6 @@ public class DebuggerThreadsProvider extends ComponentProviderAdapter {
return true;
}
- protected class StepSnapBackwardAction extends AbstractStepSnapBackwardAction {
- public static final String GROUP = DebuggerResources.GROUP_CONTROL;
-
- public StepSnapBackwardAction() {
- super(plugin);
- setToolBarData(new ToolBarData(ICON, GROUP, "1"));
- addLocalAction(this);
- setEnabled(false);
- }
-
- @Override
- public void actionPerformed(ActionContext context) {
- if (current.getTime().isSnapOnly()) {
- traceManager.activateSnap(current.getSnap() - 1);
- }
- else {
- traceManager.activateSnap(current.getSnap());
- }
- }
-
- @Override
- public boolean isEnabledForContext(ActionContext context) {
- if (current.getTrace() == null) {
- return false;
- }
- if (!current.getTime().isSnapOnly()) {
- return true;
- }
- if (current.getSnap() <= 0) {
- return false;
- }
- return true;
- }
- }
-
- protected class EmulateTickBackwardAction extends AbstractEmulateTickBackwardAction {
- public static final String GROUP = DebuggerResources.GROUP_CONTROL;
-
- public EmulateTickBackwardAction() {
- super(plugin);
- setToolBarData(new ToolBarData(ICON, GROUP, "2"));
- addLocalAction(this);
- setEnabled(false);
- }
-
- @Override
- public void actionPerformed(ActionContext context) {
- if (current.getTrace() == null) {
- return;
- }
- TraceSchedule time = current.getTime().steppedBackward(current.getTrace(), 1);
- if (time == null) {
- return;
- }
- traceManager.activateTime(time);
- }
-
- @Override
- public boolean isEnabledForContext(ActionContext context) {
- if (emulationService == null) {
- return false;
- }
- if (current.getTrace() == null) {
- return false;
- }
- if (current.getTime().steppedBackward(current.getTrace(), 1) == null) {
- return false;
- }
- return true;
- }
- }
-
- protected class EmulateTickForwardAction extends AbstractEmulateTickForwardAction {
- public static final String GROUP = DebuggerResources.GROUP_CONTROL;
-
- public EmulateTickForwardAction() {
- super(plugin);
- setToolBarData(new ToolBarData(ICON, GROUP, "3"));
- addLocalAction(this);
- setEnabled(false);
- }
-
- @Override
- public void actionPerformed(ActionContext context) {
- if (current.getThread() == null) {
- return;
- }
- TraceSchedule time = current.getTime().steppedForward(current.getThread(), 1);
- traceManager.activateTime(time);
- }
-
- @Override
- public boolean isEnabledForContext(ActionContext context) {
- if (emulationService == null) {
- return false;
- }
- if (current.getThread() == null) {
- return false;
- }
- return true;
- }
- }
-
- protected class StepSnapForwardAction extends AbstractStepSnapForwardAction {
- public static final String GROUP = DebuggerResources.GROUP_CONTROL;
-
- public StepSnapForwardAction() {
- super(plugin);
- setToolBarData(new ToolBarData(ICON, GROUP, "4"));
- addLocalAction(this);
- setEnabled(false);
- }
-
- @Override
- public void actionPerformed(ActionContext context) {
- traceManager.activateSnap(current.getSnap() + 1);
- }
-
- @Override
- public boolean isEnabledForContext(ActionContext context) {
- Trace curTrace = current.getTrace();
- if (curTrace == null) {
- return false;
- }
- Long maxSnap = curTrace.getTimeManager().getMaxSnap();
- if (maxSnap == null || current.getSnap() >= maxSnap) {
- return false;
- }
- return true;
- }
- }
-
- protected class SeekTracePresentAction extends AbstractSeekTracePresentAction
- implements BooleanChangeAdapter {
- public static final String GROUP = "zz";
-
- public SeekTracePresentAction() {
- super(plugin);
- setToolBarData(new ToolBarData(ICON, GROUP));
- addLocalAction(this);
- setSelected(traceManager == null ? false : traceManager.isAutoActivatePresent());
- traceManager.addAutoActivatePresentChangeListener(this);
- }
-
- @Override
- public boolean isEnabledForContext(ActionContext context) {
- return traceManager != null;
- }
-
- @Override
- public void actionPerformed(ActionContext context) {
- if (traceManager == null) {
- return;
- }
- traceManager.setAutoActivatePresent(isSelected());
- }
-
- @Override
- public void changed(Boolean value) {
- if (isSelected() == value) {
- return;
- }
- setSelected(value);
- }
- }
-
protected static class ThreadTableModel
extends RowWrappedEnumeratedColumnTableModel< //
ThreadTableColumns, ObjectKey, ThreadRow, TraceThread> {
@@ -321,9 +155,9 @@ public class DebuggerThreadsProvider extends ComponentProviderAdapter {
private final DebuggerThreadsPlugin plugin;
- // @AutoServiceConsumed by method
+ // @AutoServiceConsumed by method
private DebuggerModelService modelService;
- @AutoServiceConsumed // NB, also by method
+ // @AutoServiceConsumed by method
private DebuggerTraceManagerService traceManager;
@AutoServiceConsumed // NB, also by method
private DebuggerEmulationService emulationService;
@@ -337,6 +171,10 @@ public class DebuggerThreadsProvider extends ComponentProviderAdapter {
private final ThreadsListener threadsListener = new ThreadsListener();
private final CollectionChangeListener recordersListener =
new RecordersChangeListener();
+ private final BooleanChangeAdapter activatePresentChangeListener =
+ this::changedAutoActivatePresent;
+ private final BooleanChangeAdapter synchronizeFocusChangeListener =
+ this::changedSynchronizeFocus;
/* package access for testing */
final RangeTableCellRenderer rangeRenderer = new RangeTableCellRenderer<>();
final RangeCursorTableHeaderRenderer headerRenderer =
@@ -354,11 +192,12 @@ public class DebuggerThreadsProvider extends ComponentProviderAdapter {
private ActionContext myActionContext;
DockingAction actionSaveTrace;
- StepSnapBackwardAction actionStepSnapBackward;
- EmulateTickBackwardAction actionEmulateTickBackward;
- EmulateTickForwardAction actionEmulateTickForward;
- StepSnapForwardAction actionStepSnapForward;
- SeekTracePresentAction actionSeekTracePresent;
+ DockingAction actionStepSnapBackward;
+ DockingAction actionEmulateTickBackward;
+ DockingAction actionEmulateTickForward;
+ DockingAction actionEmulateTickSkipForward;
+ DockingAction actionStepSnapForward;
+ ToggleDockingAction actionSeekTracePresent;
ToggleDockingAction actionSyncFocus;
DockingAction actionGoToTime;
@@ -410,9 +249,21 @@ public class DebuggerThreadsProvider extends ComponentProviderAdapter {
@AutoServiceConsumed
public void setTraceManager(DebuggerTraceManagerService traceManager) {
- if (traceManager != null && actionSeekTracePresent != null) {
- actionSeekTracePresent.setSelected(traceManager.isAutoActivatePresent());
- actionSyncFocus.setSelected(traceManager.isSynchronizeFocus());
+ if (this.traceManager != null) {
+ this.traceManager
+ .removeAutoActivatePresentChangeListener(activatePresentChangeListener);
+ this.traceManager.removeSynchronizeFocusChangeListener(synchronizeFocusChangeListener);
+ }
+ this.traceManager = traceManager;
+ if (traceManager != null) {
+ traceManager.addAutoActivatePresentChangeListener(activatePresentChangeListener);
+ traceManager.addSynchronizeFocusChangeListener(synchronizeFocusChangeListener);
+ if (actionSeekTracePresent != null) {
+ actionSeekTracePresent.setSelected(traceManager.isAutoActivatePresent());
+ }
+ if (actionSyncFocus != null) {
+ actionSyncFocus.setSelected(traceManager.isSynchronizeFocus());
+ }
}
contextChanged();
}
@@ -633,11 +484,34 @@ public class DebuggerThreadsProvider extends ComponentProviderAdapter {
protected void createActions() {
// TODO: Make other actions use builder?
- actionStepSnapBackward = new StepSnapBackwardAction();
- actionEmulateTickBackward = new EmulateTickBackwardAction();
- actionEmulateTickForward = new EmulateTickForwardAction();
- actionStepSnapForward = new StepSnapForwardAction();
- actionSeekTracePresent = new SeekTracePresentAction();
+ actionStepSnapBackward = StepSnapBackwardAction.builder(plugin)
+ .enabledWhen(this::isStepSnapBackwardEnabled)
+ .enabled(false)
+ .onAction(this::activatedStepSnapBackward)
+ .buildAndInstallLocal(this);
+ actionEmulateTickBackward = EmulateTickBackwardAction.builder(plugin)
+ .enabledWhen(this::isEmulateTickBackwardEnabled)
+ .onAction(this::activatedEmulateTickBackward)
+ .buildAndInstallLocal(this);
+ actionEmulateTickForward = EmulateTickForwardAction.builder(plugin)
+ .enabledWhen(this::isEmulateTickForwardEnabled)
+ .onAction(this::activatedEmulateTickForward)
+ .buildAndInstallLocal(this);
+ actionEmulateTickSkipForward = EmulateSkipTickForwardAction.builder(plugin)
+ .enabledWhen(this::isEmulateSkipTickForwardEnabled)
+ .onAction(this::activatedEmulateSkipTickForward)
+ .buildAndInstallLocal(this);
+ actionStepSnapForward = StepSnapForwardAction.builder(plugin)
+ .enabledWhen(this::isStepSnapForwardEnabled)
+ .enabled(false)
+ .onAction(this::activatedStepSnapForward)
+ .buildAndInstallLocal(this);
+ actionSeekTracePresent = SeekTracePresentAction.builder(plugin)
+ .enabledWhen(this::isSeekTracePresentEnabled)
+ .onAction(this::toggledSeekTracePresent)
+ .selected(traceManager == null ? false : traceManager.isAutoActivatePresent())
+ .buildAndInstallLocal(this);
+
actionSyncFocus = SynchronizeFocusAction.builder(plugin)
.selected(traceManager != null && traceManager.isSynchronizeFocus())
.enabledWhen(c -> traceManager != null)
@@ -672,6 +546,129 @@ public class DebuggerThreadsProvider extends ComponentProviderAdapter {
.buildAndInstallLocal(this);
}
+ private boolean isStepSnapBackwardEnabled(ActionContext context) {
+ if (current.getTrace() == null) {
+ return false;
+ }
+ if (!current.getTime().isSnapOnly()) {
+ return true;
+ }
+ if (current.getSnap() <= 0) {
+ return false;
+ }
+ return true;
+ }
+
+ private void activatedStepSnapBackward(ActionContext context) {
+ if (current.getTime().isSnapOnly()) {
+ traceManager.activateSnap(current.getSnap() - 1);
+ }
+ else {
+ traceManager.activateSnap(current.getSnap());
+ }
+ }
+
+ private boolean isEmulateTickBackwardEnabled(ActionContext context) {
+ if (emulationService == null) {
+ return false;
+ }
+ if (current.getTrace() == null) {
+ return false;
+ }
+ if (current.getTime().steppedBackward(current.getTrace(), 1) == null) {
+ return false;
+ }
+ return true;
+ }
+
+ private void activatedEmulateTickBackward(ActionContext context) {
+ if (current.getTrace() == null) {
+ return;
+ }
+ TraceSchedule time = current.getTime().steppedBackward(current.getTrace(), 1);
+ if (time == null) {
+ return;
+ }
+ traceManager.activateTime(time);
+ }
+
+ private boolean isEmulateTickForwardEnabled(ActionContext context) {
+ if (emulationService == null) {
+ return false;
+ }
+ if (current.getThread() == null) {
+ return false;
+ }
+ return true;
+ }
+
+ private void activatedEmulateTickForward(ActionContext context) {
+ if (current.getThread() == null) {
+ return;
+ }
+ TraceSchedule time = current.getTime().steppedForward(current.getThread(), 1);
+ traceManager.activateTime(time);
+ }
+
+ private boolean isEmulateSkipTickForwardEnabled(ActionContext context) {
+ if (emulationService == null) {
+ return false;
+ }
+ if (current.getThread() == null) {
+ return false;
+ }
+ return true;
+ }
+
+ private void activatedEmulateSkipTickForward(ActionContext context) {
+ if (current.getThread() == null) {
+ return;
+ }
+ TraceSchedule time = current.getTime().skippedForward(current.getThread(), 1);
+ traceManager.activateTime(time);
+ }
+
+ private boolean isStepSnapForwardEnabled(ActionContext context) {
+ Trace curTrace = current.getTrace();
+ if (curTrace == null) {
+ return false;
+ }
+ Long maxSnap = curTrace.getTimeManager().getMaxSnap();
+ if (maxSnap == null || current.getSnap() >= maxSnap) {
+ return false;
+ }
+ return true;
+ }
+
+ private void activatedStepSnapForward(ActionContext contetxt) {
+ traceManager.activateSnap(current.getSnap() + 1);
+ }
+
+ private boolean isSeekTracePresentEnabled(ActionContext context) {
+ return traceManager != null;
+ }
+
+ private void toggledSeekTracePresent(ActionContext context) {
+ if (traceManager == null) {
+ return;
+ }
+ traceManager.setAutoActivatePresent(actionSeekTracePresent.isSelected());
+ }
+
+ private void changedAutoActivatePresent(boolean value) {
+ if (actionSeekTracePresent == null || actionSeekTracePresent.isSelected()) {
+ return;
+ }
+ actionSeekTracePresent.setSelected(value);
+ }
+
+ private void changedSynchronizeFocus(boolean value) {
+ if (actionSyncFocus == null || actionSyncFocus.isSelected()) {
+ return;
+ }
+ actionSyncFocus.setSelected(value);
+ }
+
private void toggleSyncFocus(boolean enabled) {
if (traceManager == null) {
return;
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/time/schedule/AbstractStep.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/time/schedule/AbstractStep.java
new file mode 100644
index 0000000000..4ecab29ae2
--- /dev/null
+++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/time/schedule/AbstractStep.java
@@ -0,0 +1,164 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ghidra.trace.model.time.schedule;
+
+import java.util.List;
+
+import ghidra.program.model.lang.Language;
+
+public abstract class AbstractStep implements Step {
+ protected final long threadKey;
+ protected long tickCount;
+
+ protected AbstractStep(long threadKey, long tickCount) {
+ if (tickCount < 0) {
+ throw new IllegalArgumentException("Cannot step a negative number");
+ }
+ this.threadKey = threadKey;
+ this.tickCount = tickCount;
+ }
+
+ /**
+ * Return the step portion of {@link #toString()}
+ *
+ * @return the string
+ */
+ protected abstract String toStringStepPart();
+
+ @Override
+ public String toString() {
+ if (threadKey == -1) {
+ return toStringStepPart();
+ }
+ return String.format("t%d-", threadKey) + toStringStepPart();
+ }
+
+ @Override
+ public boolean isNop() {
+ return tickCount == 0;
+ }
+
+ @Override
+ public long getThreadKey() {
+ return threadKey;
+ }
+
+ @Override
+ public long getTickCount() {
+ return tickCount;
+ }
+
+ @Override
+ public long getPatchCount() {
+ return 0;
+ }
+
+ @Override
+ public abstract AbstractStep clone();
+
+ /**
+ * Add to the count of this step
+ *
+ * @param steps the count to add
+ */
+ public void advance(long steps) {
+ if (steps < 0) {
+ throw new IllegalArgumentException("Cannot advance a negative number");
+ }
+ long newCount = tickCount + steps;
+ if (newCount < 0) {
+ throw new IllegalArgumentException("Total step count exceeds LONG_MAX");
+ }
+ this.tickCount = newCount;
+ }
+
+ @Override
+ public long rewind(long steps) {
+ if (steps < 0) {
+ throw new IllegalArgumentException("Cannot rewind a negative number");
+ }
+ long diff = this.tickCount - steps;
+ this.tickCount = Long.max(0, diff);
+ return -diff;
+ }
+
+ @Override
+ public boolean isCompatible(Step step) {
+ if (!(step.getClass() == this.getClass())) {
+ return false;
+ }
+ AbstractStep as = (AbstractStep) step;
+ return this.threadKey == as.threadKey || as.threadKey == -1;
+ }
+
+ @Override
+ public void addTo(Step step) {
+ assert isCompatible(step);
+ AbstractStep as = (AbstractStep) step;
+ advance(as.tickCount);
+ }
+
+ @Override
+ public int hashCode() {
+ return Long.hashCode(threadKey) * 31 + Long.hashCode(tickCount);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj.getClass() != this.getClass()) {
+ return false;
+ }
+ AbstractStep that = (AbstractStep) obj;
+ if (this.threadKey != that.threadKey) {
+ return false;
+ }
+ if (this.tickCount != that.tickCount) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public CompareResult compareStep(Step step) {
+ CompareResult result;
+
+ result = compareStepType(step);
+ if (result != CompareResult.EQUALS) {
+ return result;
+ }
+
+ AbstractStep that = (AbstractStep) step;
+ result = CompareResult.unrelated(Long.compare(this.threadKey, that.threadKey));
+ if (result != CompareResult.EQUALS) {
+ return result;
+ }
+
+ result = CompareResult.related(Long.compare(this.tickCount, that.tickCount));
+ if (result != CompareResult.EQUALS) {
+ return result;
+ }
+
+ return CompareResult.EQUALS;
+ }
+
+ @Override
+ public long coalescePatches(Language language, List steps) {
+ return 0;
+ }
+}
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/time/schedule/PatchStep.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/time/schedule/PatchStep.java
index aef947e2b6..6dd5d8087a 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/time/schedule/PatchStep.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/time/schedule/PatchStep.java
@@ -18,7 +18,6 @@ package ghidra.trace.model.time.schedule;
import java.math.BigInteger;
import java.util.*;
import java.util.Map.Entry;
-import java.util.function.Consumer;
import java.util.stream.Stream;
import javax.help.UnsupportedOperationException;
@@ -234,9 +233,8 @@ public class PatchStep implements Step {
}
@Override
- public int getTypeOrder() {
- // When comparing sequences, those with sleigh steps are ordered after those with ticks
- return 10;
+ public StepType getType() {
+ return StepType.PATCH;
}
@Override
@@ -314,8 +312,8 @@ public class PatchStep implements Step {
}
@Override
- public void execute(PcodeThread emuThread, Consumer> stepAction,
- TaskMonitor monitor) throws CancelledException {
+ public void execute(PcodeThread emuThread, Stepper stepper, TaskMonitor monitor)
+ throws CancelledException {
PcodeProgram prog = emuThread.getMachine().compileSleigh("schedule", List.of(sleigh + ";"));
emuThread.getExecutor().execute(prog, emuThread.getUseropLibrary());
}
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/time/schedule/Sequence.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/time/schedule/Sequence.java
index 52c20d715c..3f30d30e44 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/time/schedule/Sequence.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/time/schedule/Sequence.java
@@ -16,13 +16,11 @@
package ghidra.trace.model.time.schedule;
import java.util.*;
-import java.util.function.Consumer;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import ghidra.pcode.emu.PcodeMachine;
-import ghidra.pcode.emu.PcodeThread;
import ghidra.program.model.lang.Language;
import ghidra.trace.model.Trace;
import ghidra.trace.model.thread.TraceThread;
@@ -381,17 +379,17 @@ public class Sequence implements Comparable {
* @param trace the trace to which the machine is bound
* @param eventThread the thread for the first step, if it applies to the "last thread"
* @param machine the machine to step, or null to validate the sequence
- * @param action the action to step each thread
+ * @param stepper the actions to step each thread
* @param monitor a monitor for cancellation and progress reports
* @return the last trace thread stepped during execution
* @throws CancelledException if execution is cancelled
*/
public TraceThread execute(Trace trace, TraceThread eventThread, PcodeMachine machine,
- Consumer> action, TaskMonitor monitor) throws CancelledException {
+ Stepper stepper, TaskMonitor monitor) throws CancelledException {
TraceThreadManager tm = trace.getThreadManager();
TraceThread thread = eventThread;
for (Step step : steps) {
- thread = step.execute(tm, thread, machine, action, monitor);
+ thread = step.execute(tm, thread, machine, stepper, monitor);
}
return thread;
}
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/time/schedule/SkipStep.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/time/schedule/SkipStep.java
new file mode 100644
index 0000000000..f2e2797f21
--- /dev/null
+++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/time/schedule/SkipStep.java
@@ -0,0 +1,77 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ghidra.trace.model.time.schedule;
+
+import ghidra.pcode.emu.PcodeThread;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.task.TaskMonitor;
+
+public class SkipStep extends AbstractStep {
+
+ public static SkipStep parse(long threadKey, String stepSpec) {
+ if (!stepSpec.startsWith("s")) {
+ throw new IllegalArgumentException("Cannot parse skip step: '" + stepSpec + "'");
+ }
+ try {
+ return new SkipStep(threadKey, Long.parseLong(stepSpec.substring(1)));
+ }
+ catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Cannot parse skip step: '" + stepSpec + "'");
+ }
+ }
+
+ /**
+ * Construct a skip step for the given thread with the given tick count
+ *
+ * @param threadKey the key of the thread in the trace, -1 for the "last thread"
+ * @param tickCount the number of ticks to skip on the thread
+ */
+ public SkipStep(long threadKey, long tickCount) {
+ super(threadKey, tickCount);
+ }
+
+ @Override
+ public StepType getType() {
+ return StepType.SKIP;
+ }
+
+ @Override
+ protected String toStringStepPart() {
+ return String.format("s%d", tickCount);
+ }
+
+ @Override
+ public AbstractStep clone() {
+ return new SkipStep(threadKey, tickCount);
+ }
+
+ @Override
+ public Step subtract(Step step) {
+ assert isCompatible(step);
+ SkipStep that = (SkipStep) step;
+ return new SkipStep(this.threadKey, this.tickCount - that.tickCount);
+ }
+
+ @Override
+ public void execute(PcodeThread emuThread, Stepper stepper, TaskMonitor monitor)
+ throws CancelledException {
+ for (int i = 0; i < tickCount; i++) {
+ monitor.incrementProgress(1);
+ monitor.checkCanceled();
+ stepper.skip(emuThread);
+ }
+ }
+}
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/time/schedule/Step.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/time/schedule/Step.java
index 631f6ad8f4..56dabec3b3 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/time/schedule/Step.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/time/schedule/Step.java
@@ -16,7 +16,6 @@
package ghidra.trace.model.time.schedule;
import java.util.List;
-import java.util.function.Consumer;
import ghidra.pcode.emu.PcodeMachine;
import ghidra.pcode.emu.PcodeThread;
@@ -27,6 +26,12 @@ import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
public interface Step extends Comparable {
+ enum StepType {
+ TICK,
+ SKIP,
+ PATCH,
+ }
+
/**
* Parse a step, possibly including a thread prefix, e.g., {@code "t1-..."}
*
@@ -69,6 +74,9 @@ public interface Step extends Comparable {
* @throws IllegalArgumentException if the specification is of the wrong form
*/
static Step parse(long threadKey, String stepSpec) {
+ if (stepSpec.startsWith("s")) {
+ return SkipStep.parse(threadKey, stepSpec);
+ }
if (stepSpec.startsWith("{")) {
return PatchStep.parse(threadKey, stepSpec);
}
@@ -79,7 +87,11 @@ public interface Step extends Comparable {
return new TickStep(-1, 0);
}
- int getTypeOrder();
+ StepType getType();
+
+ default int getTypeOrder() {
+ return getType().ordinal();
+ }
boolean isNop();
@@ -159,7 +171,7 @@ public interface Step extends Comparable {
}
default TraceThread execute(TraceThreadManager tm, TraceThread eventThread,
- PcodeMachine machine, Consumer> stepAction, TaskMonitor monitor)
+ PcodeMachine machine, Stepper stepper, TaskMonitor monitor)
throws CancelledException {
TraceThread thread = getThread(tm, eventThread);
if (machine == null) {
@@ -167,12 +179,12 @@ public interface Step extends Comparable {
return thread;
}
PcodeThread emuThread = machine.getThread(thread.getPath(), true);
- execute(emuThread, stepAction, monitor);
+ execute(emuThread, stepper, monitor);
return thread;
}
- void execute(PcodeThread emuThread, Consumer> stepAction,
- TaskMonitor monitor) throws CancelledException;
+ void execute(PcodeThread emuThread, Stepper stepper, TaskMonitor monitor)
+ throws CancelledException;
long coalescePatches(Language language, List steps);
}
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/time/schedule/Stepper.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/time/schedule/Stepper.java
new file mode 100644
index 0000000000..fc21b6865b
--- /dev/null
+++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/time/schedule/Stepper.java
@@ -0,0 +1,60 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ghidra.trace.model.time.schedule;
+
+import ghidra.pcode.emu.PcodeThread;
+
+public interface Stepper {
+ @SuppressWarnings("rawtypes")
+ enum Enum implements Stepper {
+ INSTRUCTION {
+ @Override
+ public void tick(PcodeThread thread) {
+ thread.stepInstruction();
+ }
+
+ @Override
+ public void skip(PcodeThread thread) {
+ thread.skipInstruction();
+ }
+ },
+ PCODE {
+ @Override
+ public void tick(PcodeThread thread) {
+ thread.stepPcodeOp();
+ }
+
+ @Override
+ public void skip(PcodeThread thread) {
+ thread.skipPcodeOp();
+ }
+ };
+ }
+
+ @SuppressWarnings("unchecked")
+ static Stepper instruction() {
+ return Enum.INSTRUCTION;
+ }
+
+ @SuppressWarnings("unchecked")
+ static Stepper pcode() {
+ return Enum.PCODE;
+ }
+
+ void tick(PcodeThread thread);
+
+ void skip(PcodeThread thread);
+}
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/time/schedule/TickStep.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/time/schedule/TickStep.java
index 1aae2b99d0..27fd9bbe4d 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/time/schedule/TickStep.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/time/schedule/TickStep.java
@@ -15,76 +15,42 @@
*/
package ghidra.trace.model.time.schedule;
-import java.util.List;
-import java.util.function.Consumer;
-
import ghidra.pcode.emu.PcodeThread;
-import ghidra.program.model.lang.Language;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
/**
* A step of a given thread in a schedule: repeating some number of ticks
*/
-public class TickStep implements Step {
+public class TickStep extends AbstractStep {
public static TickStep parse(long threadKey, String stepSpec) {
try {
return new TickStep(threadKey, Long.parseLong(stepSpec));
}
catch (NumberFormatException e) {
- throw new IllegalArgumentException("Cannot parse step: '" + stepSpec + "'");
+ throw new IllegalArgumentException("Cannot parse tick step: '" + stepSpec + "'");
}
}
- protected final long threadKey;
- protected long tickCount;
-
/**
- * Construct a step for the given thread with the given tick count
+ * Construct a tick step for the given thread with the given tick count
*
* @param threadKey the key of the thread in the trace, -1 for the "last thread"
- * @param tickCount the number of times to step the thread
+ * @param tickCount the number of ticks to step on the thread
*/
public TickStep(long threadKey, long tickCount) {
- if (tickCount < 0) {
- throw new IllegalArgumentException("Cannot step a negative number");
- }
- this.threadKey = threadKey;
- this.tickCount = tickCount;
+ super(threadKey, tickCount);
}
@Override
- public int getTypeOrder() {
- return 0;
+ public StepType getType() {
+ return StepType.TICK;
}
@Override
- public String toString() {
- if (threadKey == -1) {
- return Long.toString(tickCount);
- }
- return String.format("t%d-%d", threadKey, tickCount);
- }
-
- @Override
- public boolean isNop() {
- return tickCount == 0;
- }
-
- @Override
- public long getThreadKey() {
- return threadKey;
- }
-
- @Override
- public long getTickCount() {
- return tickCount;
- }
-
- @Override
- public long getPatchCount() {
- return 0;
+ protected String toStringStepPart() {
+ return Long.toString(tickCount);
}
@Override
@@ -92,48 +58,6 @@ public class TickStep implements Step {
return new TickStep(threadKey, tickCount);
}
- /**
- * Add to the count of this step
- *
- * @param steps the count to add
- */
- public void advance(long steps) {
- if (steps < 0) {
- throw new IllegalArgumentException("Cannot advance a negative number");
- }
- long newCount = tickCount + steps;
- if (newCount < 0) {
- throw new IllegalArgumentException("Total step count exceeds LONG_MAX");
- }
- this.tickCount = newCount;
- }
-
- @Override
- public long rewind(long steps) {
- if (steps < 0) {
- throw new IllegalArgumentException("Cannot rewind a negative number");
- }
- long diff = this.tickCount - steps;
- this.tickCount = Long.max(0, diff);
- return -diff;
- }
-
- @Override
- public boolean isCompatible(Step step) {
- if (!(step instanceof TickStep)) {
- return false;
- }
- TickStep ts = (TickStep) step;
- return this.threadKey == ts.threadKey || ts.threadKey == -1;
- }
-
- @Override
- public void addTo(Step step) {
- assert isCompatible(step);
- TickStep ts = (TickStep) step;
- advance(ts.tickCount);
- }
-
@Override
public Step subtract(Step step) {
assert isCompatible(step);
@@ -142,63 +66,12 @@ public class TickStep implements Step {
}
@Override
- public int hashCode() {
- return Long.hashCode(threadKey) * 31 + Long.hashCode(tickCount);
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (!(obj instanceof TickStep)) {
- return false;
- }
- TickStep that = (TickStep) obj;
- if (this.threadKey != that.threadKey) {
- return false;
- }
- if (this.tickCount != that.tickCount) {
- return false;
- }
- return true;
- }
-
- @Override
- public CompareResult compareStep(Step step) {
- CompareResult result;
-
- result = compareStepType(step);
- if (result != CompareResult.EQUALS) {
- return result;
- }
-
- TickStep that = (TickStep) step;
- result = CompareResult.unrelated(Long.compare(this.threadKey, that.threadKey));
- if (result != CompareResult.EQUALS) {
- return result;
- }
-
- result = CompareResult.related(Long.compare(this.tickCount, that.tickCount));
- if (result != CompareResult.EQUALS) {
- return result;
- }
-
- return CompareResult.EQUALS;
- }
-
- @Override
- public void execute(PcodeThread emuThread, Consumer> stepAction,
- TaskMonitor monitor) throws CancelledException {
+ public void execute(PcodeThread emuThread, Stepper stepper, TaskMonitor monitor)
+ throws CancelledException {
for (int i = 0; i < tickCount; i++) {
monitor.incrementProgress(1);
monitor.checkCanceled();
- stepAction.accept(emuThread);
+ stepper.tick(emuThread);
}
}
-
- @Override
- public long coalescePatches(Language language, List steps) {
- return 0;
- }
}
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/time/schedule/TraceSchedule.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/time/schedule/TraceSchedule.java
index c429ec5a12..e8d9b60386 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/time/schedule/TraceSchedule.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/time/schedule/TraceSchedule.java
@@ -18,7 +18,6 @@ package ghidra.trace.model.time.schedule;
import java.util.*;
import ghidra.pcode.emu.PcodeMachine;
-import ghidra.pcode.emu.PcodeThread;
import ghidra.trace.model.Trace;
import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.model.time.TraceSnapshot;
@@ -339,9 +338,9 @@ public class TraceSchedule implements Comparable {
throws CancelledException {
TraceThread lastThread = getEventThread(trace);
lastThread =
- steps.execute(trace, lastThread, machine, PcodeThread::stepInstruction, monitor);
+ steps.execute(trace, lastThread, machine, Stepper.instruction(), monitor);
lastThread =
- pSteps.execute(trace, lastThread, machine, PcodeThread::stepPcodeOp, monitor);
+ pSteps.execute(trace, lastThread, machine, Stepper.pcode(), monitor);
}
/**
@@ -383,13 +382,13 @@ public class TraceSchedule implements Comparable {
if (remains.isNop()) {
Sequence pRemains = this.pSteps.relativize(position.pSteps);
lastThread =
- pRemains.execute(trace, lastThread, machine, PcodeThread::stepPcodeOp, monitor);
+ pRemains.execute(trace, lastThread, machine, Stepper.pcode(), monitor);
}
else {
lastThread =
- remains.execute(trace, lastThread, machine, PcodeThread::stepInstruction, monitor);
+ remains.execute(trace, lastThread, machine, Stepper.instruction(), monitor);
lastThread =
- pSteps.execute(trace, lastThread, machine, PcodeThread::stepPcodeOp, monitor);
+ pSteps.execute(trace, lastThread, machine, Stepper.pcode(), monitor);
}
}
@@ -411,6 +410,19 @@ public class TraceSchedule implements Comparable {
return new TraceSchedule(snap, steps, new Sequence());
}
+ /**
+ * Behaves as in {@link #steppedForward(TraceThread, long)}, but by appending skips
+ *
+ * @param thread the thread to step, or null for the "last thread"
+ * @param tickCount the number of skips to take the thread forward
+ * @return the resulting schedule
+ */
+ public TraceSchedule skippedForward(TraceThread thread, long tickCount) {
+ Sequence steps = this.steps.clone();
+ steps.advance(new SkipStep(thread == null ? -1 : thread.getKey(), tickCount));
+ return new TraceSchedule(snap, steps, new Sequence());
+ }
+
protected TraceSchedule doSteppedBackward(Trace trace, long tickCount, Set visited) {
if (!visited.add(snap)) {
return null;
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/model/time/schedule/TestMachine.java b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/model/time/schedule/TestMachine.java
new file mode 100644
index 0000000000..0d213ad1dc
--- /dev/null
+++ b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/model/time/schedule/TestMachine.java
@@ -0,0 +1,52 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ghidra.trace.model.time.schedule;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import ghidra.pcode.emu.AbstractPcodeMachine;
+import ghidra.pcode.emu.PcodeThread;
+import ghidra.pcode.exec.PcodeExecutorState;
+import ghidra.pcode.exec.PcodeUseropLibrary;
+
+class TestMachine extends AbstractPcodeMachine {
+ protected final List record = new ArrayList<>();
+
+ public TestMachine() {
+ super(TraceScheduleTest.TOY_BE_64_LANG, null);
+ }
+
+ @Override
+ protected PcodeThread createThread(String name) {
+ return new TestThread(name, this);
+ }
+
+ @Override
+ protected PcodeExecutorState createSharedState() {
+ return null;
+ }
+
+ @Override
+ protected PcodeExecutorState createLocalState(PcodeThread thread) {
+ return null;
+ }
+
+ @Override
+ protected PcodeUseropLibrary createUseropLibrary() {
+ return PcodeUseropLibrary.nil();
+ }
+}
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/model/time/schedule/TestThread.java b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/model/time/schedule/TestThread.java
new file mode 100644
index 0000000000..5aec1429ca
--- /dev/null
+++ b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/model/time/schedule/TestThread.java
@@ -0,0 +1,162 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ghidra.trace.model.time.schedule;
+
+import java.util.List;
+
+import ghidra.pcode.emu.PcodeThread;
+import ghidra.pcode.emu.ThreadPcodeExecutorState;
+import ghidra.pcode.exec.*;
+import ghidra.program.model.address.Address;
+import ghidra.program.model.lang.RegisterValue;
+import ghidra.program.model.listing.Instruction;
+
+class TestThread implements PcodeThread {
+ protected final String name;
+ protected final TestMachine machine;
+
+ public TestThread(String name, TestMachine machine) {
+ this.name = name;
+ this.machine = machine;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public TestMachine getMachine() {
+ return machine;
+ }
+
+ @Override
+ public PcodeExecutor getExecutor() {
+ return new PcodeExecutor<>(TraceScheduleTest.TOY_BE_64_LANG, machine.getArithmetic(), getState()) {
+ public PcodeFrame execute(PcodeProgram program, PcodeUseropLibrary library) {
+ machine.record.add("x:" + name);
+ // TODO: Verify the actual effect
+ return null; //super.execute(program, library);
+ }
+ };
+ }
+
+ @Override
+ public void stepInstruction() {
+ machine.record.add("ti:" + name);
+ }
+
+ @Override
+ public void skipInstruction() {
+ machine.record.add("si:" + name);
+ }
+
+ @Override
+ public void stepPcodeOp() {
+ machine.record.add("tp:" + name);
+ }
+
+ @Override
+ public void skipPcodeOp() {
+ machine.record.add("sp:" + name);
+ }
+
+ @Override
+ public void setCounter(Address counter) {
+ }
+
+ @Override
+ public Address getCounter() {
+ return null;
+ }
+
+ @Override
+ public void overrideCounter(Address counter) {
+ }
+
+ @Override
+ public void assignContext(RegisterValue context) {
+ }
+
+ @Override
+ public RegisterValue getContext() {
+ return null;
+ }
+
+ @Override
+ public void overrideContext(RegisterValue context) {
+ }
+
+ @Override
+ public void overrideContextWithDefault() {
+ }
+
+ @Override
+ public void reInitialize() {
+ }
+
+ @Override
+ public PcodeFrame getFrame() {
+ return null;
+ }
+
+ @Override
+ public Instruction getInstruction() {
+ return null;
+ }
+
+ @Override
+ public void executeInstruction() {
+ }
+
+ @Override
+ public void finishInstruction() {
+ }
+
+ @Override
+ public void dropInstruction() {
+ }
+
+ @Override
+ public void run() {
+ }
+
+ @Override
+ public void setSuspended(boolean suspended) {
+ }
+
+ @Override
+ public PcodeUseropLibrary getUseropLibrary() {
+ return null;
+ }
+
+ @Override
+ public ThreadPcodeExecutorState getState() {
+ return null;
+ }
+
+ @Override
+ public void inject(Address address, List sleigh) {
+ }
+
+ @Override
+ public void clearInject(Address address) {
+ }
+
+ @Override
+ public void clearAllInjects() {
+ }
+}
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/model/time/schedule/TraceScheduleTest.java b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/model/time/schedule/TraceScheduleTest.java
index b5ff70cb7e..ee2613fb07 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/model/time/schedule/TraceScheduleTest.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/model/time/schedule/TraceScheduleTest.java
@@ -17,18 +17,14 @@ package ghidra.trace.model.time.schedule;
import static org.junit.Assert.*;
-import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
-import ghidra.pcode.emu.*;
-import ghidra.pcode.exec.*;
-import ghidra.program.model.address.Address;
-import ghidra.program.model.lang.*;
-import ghidra.program.model.listing.Instruction;
+import ghidra.program.model.lang.LanguageID;
+import ghidra.program.model.lang.LanguageNotFoundException;
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
import ghidra.test.ToyProgramBuilder;
import ghidra.trace.database.ToyDBTraceBuilder;
@@ -267,165 +263,6 @@ public class TraceScheduleTest extends AbstractGhidraHeadlessIntegrationTest {
assertEquals(15, TraceSchedule.parse("0:4;t1-5.6").totalTickCount());
}
- protected static class TestThread implements PcodeThread {
- protected final String name;
- protected final TestMachine machine;
-
- public TestThread(String name, TestMachine machine) {
- this.name = name;
- this.machine = machine;
- }
-
- @Override
- public String getName() {
- return name;
- }
-
- @Override
- public TestMachine getMachine() {
- return machine;
- }
-
- @Override
- public void setCounter(Address counter) {
- }
-
- @Override
- public Address getCounter() {
- return null;
- }
-
- @Override
- public void overrideCounter(Address counter) {
- }
-
- @Override
- public void assignContext(RegisterValue context) {
- }
-
- @Override
- public RegisterValue getContext() {
- return null;
- }
-
- @Override
- public void overrideContext(RegisterValue context) {
- }
-
- @Override
- public void overrideContextWithDefault() {
- }
-
- @Override
- public void reInitialize() {
- }
-
- @Override
- public void stepInstruction() {
- machine.record.add("s:" + name);
- }
-
- @Override
- public void stepPcodeOp() {
- machine.record.add("p:" + name);
- }
-
- @Override
- public PcodeFrame getFrame() {
- return null;
- }
-
- @Override
- public Instruction getInstruction() {
- return null;
- }
-
- @Override
- public void executeInstruction() {
- }
-
- @Override
- public void finishInstruction() {
- }
-
- @Override
- public void skipInstruction() {
- }
-
- @Override
- public void dropInstruction() {
- }
-
- @Override
- public void run() {
- }
-
- @Override
- public void setSuspended(boolean suspended) {
- }
-
- @Override
- public PcodeExecutor getExecutor() {
- return new PcodeExecutor<>(TOY_BE_64_LANG, machine.getArithmetic(), getState()) {
- public PcodeFrame execute(PcodeProgram program, PcodeUseropLibrary library) {
- machine.record.add("x:" + name);
- // TODO: Verify the actual effect
- return null; //super.execute(program, library);
- }
- };
- }
-
- @Override
- public PcodeUseropLibrary getUseropLibrary() {
- return null;
- }
-
- @Override
- public ThreadPcodeExecutorState getState() {
- return null;
- }
-
- @Override
- public void inject(Address address, List sleigh) {
- }
-
- @Override
- public void clearInject(Address address) {
- }
-
- @Override
- public void clearAllInjects() {
- }
- }
-
- protected static class TestMachine extends AbstractPcodeMachine {
- protected final List record = new ArrayList<>();
-
- public TestMachine() {
- super(TOY_BE_64_LANG, null);
- }
-
- @Override
- protected PcodeThread createThread(String name) {
- return new TestThread(name, this);
- }
-
- @Override
- protected PcodeExecutorState createSharedState() {
- return null;
- }
-
- @Override
- protected PcodeExecutorState createLocalState(PcodeThread thread) {
- return null;
- }
-
- @Override
- protected PcodeUseropLibrary createUseropLibrary() {
- return PcodeUseropLibrary.nil();
- }
- }
-
@Test
public void testExecute() throws Exception {
TestMachine machine = new TestMachine();
@@ -442,16 +279,45 @@ public class TraceScheduleTest extends AbstractGhidraHeadlessIntegrationTest {
}
assertEquals(List.of(
- "s:Threads[2]",
- "s:Threads[2]",
- "s:Threads[2]",
- "s:Threads[2]",
- "s:Threads[0]",
- "s:Threads[0]",
- "s:Threads[0]",
- "s:Threads[1]",
- "s:Threads[1]",
- "p:Threads[1]"),
+ "ti:Threads[2]",
+ "ti:Threads[2]",
+ "ti:Threads[2]",
+ "ti:Threads[2]",
+ "ti:Threads[0]",
+ "ti:Threads[0]",
+ "ti:Threads[0]",
+ "ti:Threads[1]",
+ "ti:Threads[1]",
+ "tp:Threads[1]"),
+ machine.record);
+ }
+
+ @Test
+ public void testExecuteWithSkips() throws Exception {
+ TestMachine machine = new TestMachine();
+ TraceSchedule time = TraceSchedule.parse("1:4;t0-s3;t1-2.s1");
+ try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("test", ToyProgramBuilder._TOY64_BE)) {
+ TraceThread t2;
+ try (UndoableTransaction tid = tb.startTransaction()) {
+ tb.trace.getThreadManager().createThread("Threads[0]", 0);
+ tb.trace.getThreadManager().createThread("Threads[1]", 0);
+ t2 = tb.trace.getThreadManager().createThread("Threads[2]", 0);
+ tb.trace.getTimeManager().getSnapshot(1, true).setEventThread(t2);
+ }
+ time.execute(tb.trace, machine, TaskMonitor.DUMMY);
+ }
+
+ assertEquals(List.of(
+ "ti:Threads[2]",
+ "ti:Threads[2]",
+ "ti:Threads[2]",
+ "ti:Threads[2]",
+ "si:Threads[0]",
+ "si:Threads[0]",
+ "si:Threads[0]",
+ "ti:Threads[1]",
+ "ti:Threads[1]",
+ "sp:Threads[1]"),
machine.record);
}
@@ -472,10 +338,10 @@ public class TraceScheduleTest extends AbstractGhidraHeadlessIntegrationTest {
assertEquals(List.of(
"x:Threads[2]",
- "s:Threads[2]",
- "s:Threads[2]",
- "s:Threads[2]",
- "s:Threads[2]"),
+ "ti:Threads[2]",
+ "ti:Threads[2]",
+ "ti:Threads[2]",
+ "ti:Threads[2]"),
machine.record);
}
@@ -526,10 +392,10 @@ public class TraceScheduleTest extends AbstractGhidraHeadlessIntegrationTest {
}
assertEquals(List.of(
- "s:Threads[0]",
- "s:Threads[1]",
- "s:Threads[1]",
- "p:Threads[1]"),
+ "ti:Threads[0]",
+ "ti:Threads[1]",
+ "ti:Threads[1]",
+ "tp:Threads[1]"),
machine.record);
}
@@ -550,7 +416,7 @@ public class TraceScheduleTest extends AbstractGhidraHeadlessIntegrationTest {
}
assertEquals(List.of(
- "p:Threads[1]"),
+ "tp:Threads[1]"),
machine.record);
}
diff --git a/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/emu/DefaultPcodeThread.java b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/emu/DefaultPcodeThread.java
index 251a100bf0..91689afaf6 100644
--- a/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/emu/DefaultPcodeThread.java
+++ b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/emu/DefaultPcodeThread.java
@@ -364,6 +364,19 @@ public class DefaultPcodeThread implements PcodeThread {
}
}
+ @Override
+ public void skipPcodeOp() {
+ if (frame == null) {
+ beginInstructionOrInject();
+ }
+ else if (!frame.isFinished()) {
+ executor.skip(frame);
+ }
+ else {
+ advanceAfterFinished();
+ }
+ }
+
/**
* Start execution of the instruction or inject at the program counter
*/
diff --git a/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/emu/PcodeThread.java b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/emu/PcodeThread.java
index 5e5bb25198..ba8481576c 100644
--- a/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/emu/PcodeThread.java
+++ b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/emu/PcodeThread.java
@@ -184,6 +184,18 @@ public interface PcodeThread {
}
}
+ /**
+ * Skip emulation of a single p-code operation
+ *
+ *
+ * If there is no current frame, this behaves as in {@link #stepPcodeOp()}. Otherwise, this
+ * skips the current pcode op, advancing as if a fall-through op. If no ops remain in the frame,
+ * this behaves as in {@link #stepPcodeOp()}. Please note to skip an extranal branch, the op
+ * itself must be skipped. "Skipping" the following op, which disposes the frame, cannot prevent
+ * the branch.
+ */
+ void skipPcodeOp();
+
/**
* Get the current frame, if present
*
diff --git a/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/PcodeExecutor.java b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/PcodeExecutor.java
index 15c7f18b0c..4c15026754 100644
--- a/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/PcodeExecutor.java
+++ b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/exec/PcodeExecutor.java
@@ -211,6 +211,10 @@ public class PcodeExecutor {
}
}
+ public void skip(PcodeFrame frame) {
+ frame.nextOp();
+ }
+
protected int getIntConst(Varnode vn) {
assert vn.getAddress().getAddressSpace().isConstantSpace();
return (int) vn.getAddress().getOffset();