mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-25 05:32:14 +00:00
GP-2062: Add Skip Instruction button for emulator
This commit is contained in:
parent
00dbd26511
commit
4736a3c924
@ -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|
|
||||
|
@ -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.</P>
|
||||
|
||||
<H3><A name="emu_trace_skip_tick_forward"></A><IMG alt="" src="images/skipover.png">Emulate
|
||||
Trace Skip Tick Forward</H3>
|
||||
|
||||
<P>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; <B>however</B>, the stack may require
|
||||
additional patching, e.g., to clean up stack parameters, depending on the calling convention.
|
||||
This action <EM>does not</EM> perform those patches automatically. It only advances the program
|
||||
counter. You may use <A href="#goto_time">Go To Time</A> to append the require stack patch,
|
||||
e.g., <CODE>t0-{RSP=RSP+8}</CODE>.</P>
|
||||
|
||||
<H3><A name="seek_trace_present"></A><IMG alt="" src="images/continue.png">Seek Trace to
|
||||
Present</H3>
|
||||
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Binary file not shown.
After Width: | Height: | Size: 320 B |
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<TraceRecorder> recordersListener =
|
||||
new RecordersChangeListener();
|
||||
private final BooleanChangeAdapter activatePresentChangeListener =
|
||||
this::changedAutoActivatePresent;
|
||||
private final BooleanChangeAdapter synchronizeFocusChangeListener =
|
||||
this::changedSynchronizeFocus;
|
||||
/* package access for testing */
|
||||
final RangeTableCellRenderer<Long> rangeRenderer = new RangeTableCellRenderer<>();
|
||||
final RangeCursorTableHeaderRenderer<Long> 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;
|
||||
|
@ -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<Step> steps) {
|
||||
return 0;
|
||||
}
|
||||
}
|
@ -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 <T> void execute(PcodeThread<T> emuThread, Consumer<PcodeThread<T>> stepAction,
|
||||
TaskMonitor monitor) throws CancelledException {
|
||||
public <T> void execute(PcodeThread<T> emuThread, Stepper<T> stepper, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
PcodeProgram prog = emuThread.getMachine().compileSleigh("schedule", List.of(sleigh + ";"));
|
||||
emuThread.getExecutor().execute(prog, emuThread.getUseropLibrary());
|
||||
}
|
||||
|
@ -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<Sequence> {
|
||||
* @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 <T> TraceThread execute(Trace trace, TraceThread eventThread, PcodeMachine<T> machine,
|
||||
Consumer<PcodeThread<T>> action, TaskMonitor monitor) throws CancelledException {
|
||||
Stepper<T> 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;
|
||||
}
|
||||
|
@ -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 <T> void execute(PcodeThread<T> emuThread, Stepper<T> stepper, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
for (int i = 0; i < tickCount; i++) {
|
||||
monitor.incrementProgress(1);
|
||||
monitor.checkCanceled();
|
||||
stepper.skip(emuThread);
|
||||
}
|
||||
}
|
||||
}
|
@ -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<Step> {
|
||||
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<Step> {
|
||||
* @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<Step> {
|
||||
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<Step> {
|
||||
}
|
||||
|
||||
default <T> TraceThread execute(TraceThreadManager tm, TraceThread eventThread,
|
||||
PcodeMachine<T> machine, Consumer<PcodeThread<T>> stepAction, TaskMonitor monitor)
|
||||
PcodeMachine<T> machine, Stepper<T> stepper, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
TraceThread thread = getThread(tm, eventThread);
|
||||
if (machine == null) {
|
||||
@ -167,12 +179,12 @@ public interface Step extends Comparable<Step> {
|
||||
return thread;
|
||||
}
|
||||
PcodeThread<T> emuThread = machine.getThread(thread.getPath(), true);
|
||||
execute(emuThread, stepAction, monitor);
|
||||
execute(emuThread, stepper, monitor);
|
||||
return thread;
|
||||
}
|
||||
|
||||
<T> void execute(PcodeThread<T> emuThread, Consumer<PcodeThread<T>> stepAction,
|
||||
TaskMonitor monitor) throws CancelledException;
|
||||
<T> void execute(PcodeThread<T> emuThread, Stepper<T> stepper, TaskMonitor monitor)
|
||||
throws CancelledException;
|
||||
|
||||
long coalescePatches(Language language, List<Step> steps);
|
||||
}
|
||||
|
@ -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<T> {
|
||||
@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 <T> Stepper<T> instruction() {
|
||||
return Enum.INSTRUCTION;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static <T> Stepper<T> pcode() {
|
||||
return Enum.PCODE;
|
||||
}
|
||||
|
||||
void tick(PcodeThread<T> thread);
|
||||
|
||||
void skip(PcodeThread<T> thread);
|
||||
}
|
@ -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 <T> void execute(PcodeThread<T> emuThread, Consumer<PcodeThread<T>> stepAction,
|
||||
TaskMonitor monitor) throws CancelledException {
|
||||
public <T> void execute(PcodeThread<T> emuThread, Stepper<T> 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<Step> steps) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -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<TraceSchedule> {
|
||||
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<TraceSchedule> {
|
||||
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<TraceSchedule> {
|
||||
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<Long> visited) {
|
||||
if (!visited.add(snap)) {
|
||||
return null;
|
||||
|
@ -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<Void> {
|
||||
protected final List<String> record = new ArrayList<>();
|
||||
|
||||
public TestMachine() {
|
||||
super(TraceScheduleTest.TOY_BE_64_LANG, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PcodeThread<Void> createThread(String name) {
|
||||
return new TestThread(name, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PcodeExecutorState<Void> createSharedState() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PcodeExecutorState<Void> createLocalState(PcodeThread<Void> thread) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PcodeUseropLibrary<Void> createUseropLibrary() {
|
||||
return PcodeUseropLibrary.nil();
|
||||
}
|
||||
}
|
@ -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<Void> {
|
||||
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<Void> getExecutor() {
|
||||
return new PcodeExecutor<>(TraceScheduleTest.TOY_BE_64_LANG, machine.getArithmetic(), getState()) {
|
||||
public PcodeFrame execute(PcodeProgram program, PcodeUseropLibrary<Void> 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<Void> getUseropLibrary() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ThreadPcodeExecutorState<Void> getState() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inject(Address address, List<String> sleigh) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearInject(Address address) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearAllInjects() {
|
||||
}
|
||||
}
|
@ -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<Void> {
|
||||
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<Void> getExecutor() {
|
||||
return new PcodeExecutor<>(TOY_BE_64_LANG, machine.getArithmetic(), getState()) {
|
||||
public PcodeFrame execute(PcodeProgram program, PcodeUseropLibrary<Void> library) {
|
||||
machine.record.add("x:" + name);
|
||||
// TODO: Verify the actual effect
|
||||
return null; //super.execute(program, library);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public PcodeUseropLibrary<Void> getUseropLibrary() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ThreadPcodeExecutorState<Void> getState() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inject(Address address, List<String> sleigh) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearInject(Address address) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearAllInjects() {
|
||||
}
|
||||
}
|
||||
|
||||
protected static class TestMachine extends AbstractPcodeMachine<Void> {
|
||||
protected final List<String> record = new ArrayList<>();
|
||||
|
||||
public TestMachine() {
|
||||
super(TOY_BE_64_LANG, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PcodeThread<Void> createThread(String name) {
|
||||
return new TestThread(name, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PcodeExecutorState<Void> createSharedState() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PcodeExecutorState<Void> createLocalState(PcodeThread<Void> thread) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PcodeUseropLibrary<Void> 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);
|
||||
}
|
||||
|
||||
|
@ -364,6 +364,19 @@ public class DefaultPcodeThread<T> implements PcodeThread<T> {
|
||||
}
|
||||
}
|
||||
|
||||
@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
|
||||
*/
|
||||
|
@ -184,6 +184,18 @@ public interface PcodeThread<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip emulation of a single p-code operation
|
||||
*
|
||||
* <p>
|
||||
* 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
|
||||
*
|
||||
|
@ -211,6 +211,10 @@ public class PcodeExecutor<T> {
|
||||
}
|
||||
}
|
||||
|
||||
public void skip(PcodeFrame frame) {
|
||||
frame.nextOp();
|
||||
}
|
||||
|
||||
protected int getIntConst(Varnode vn) {
|
||||
assert vn.getAddress().getAddressSpace().isConstantSpace();
|
||||
return (int) vn.getAddress().getOffset();
|
||||
|
Loading…
Reference in New Issue
Block a user