mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-29 01:16:01 +08:00
Merge remote-tracking branch 'origin/Ghidra_10.1'
Conflicts: Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerRegionsPlugin/images/DebuggerRegionsPlugin.png Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerRegionsProvider.java
This commit is contained in:
+4
-4
@@ -22,10 +22,10 @@
|
||||
</TBODY>
|
||||
</TABLE>
|
||||
|
||||
<P>P-code is the "microcode" of Ghidra's processor specifications, compiled from its SLEIGH
|
||||
<P>P-code is the "microcode" of Ghidra's processor specifications, compiled from its Sleigh
|
||||
specification. Originally designed to facilitate static analysis, it is easily applied to
|
||||
emulation as well. Stepping each p-code operation is an effective means of debugging the
|
||||
SLEIGH. The plugin provides two panes: 1) The p-code listing, and 2) Temporary ("Unique")
|
||||
Sleigh. The plugin provides two panes: 1) The p-code listing, and 2) Temporary ("Unique")
|
||||
variables. The listing works similarly to the dynamic listing. It displays each p-code
|
||||
operation, highlighting the current "counter", which is the next operation to be executed.
|
||||
There is also a cursor, allowing selection of an operation. The variables view operates
|
||||
@@ -62,12 +62,12 @@
|
||||
interact with the target, stepping at the p-code level implies you are no longer "at the
|
||||
present."</P>
|
||||
|
||||
<H3><A name="step_trace_pcode_backward"></A>Step Trace p-code Backward</H3>
|
||||
<H3><A name="emu_trace_pcode_backward"></A>Emulate Trace p-code Backward</H3>
|
||||
|
||||
<P>This action is available when the current coordinates have some positive number of p-code
|
||||
ticks. It steps the trace backward to the previous p-code tick.</P>
|
||||
|
||||
<H3><A name="step_trace_pcode_forward"></A>Step Trace p-code Forward</H3>
|
||||
<H3><A name="emu_trace_pcode_forward"></A>Emulate Trace p-code Forward</H3>
|
||||
|
||||
<P>This action is available when a thread is selected. It steps the current thread forward to
|
||||
the next p-code tick, using emulation. Note that emulation does not affect the target.
|
||||
|
||||
+9
@@ -100,5 +100,14 @@
|
||||
<P>This action is available when the dynamic listing's cursor is at a valid location. It
|
||||
selects the region containing that cursor. If the dynamic listing has a selection, it selects
|
||||
all regions intersecting that selection.</P>
|
||||
|
||||
<H3><A name="force_full_view"></A>Force Full View</H3>
|
||||
|
||||
<P>This action is available when a trace is active. It forces all physical address spaces into
|
||||
the view. Ordinarily, only those addresses contained in a region at the active snap are
|
||||
presented in the listing and memory windows. When this toggle is on, regions are ignored.
|
||||
Instead, all physical addresses are presented. (Here "physical" includes all memory spaces
|
||||
except <CODE>OTHER</CODE>.) This toggle applies only to the current trace for the duration it
|
||||
is open.</P>
|
||||
</BODY>
|
||||
</HTML>
|
||||
|
||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 18 KiB |
+13
-7
@@ -45,9 +45,10 @@
|
||||
|
||||
<LI>Value - the value of the register as recorded in the trace. When the value refers to a
|
||||
valid memory offset, right-clicking the row allows the user to navigate to that offset in a
|
||||
selected memory space. This field is user modifiable when the target is alive, the trace is
|
||||
"at the present," and the <B>Enable Edits</B> toggle is on. Changes to the register's value
|
||||
are sent to the target. Values changed by the last event are displayed in <FONT color=
|
||||
selected memory space. This field is user modifiable when the <B>Enable Edits</B> toggle is
|
||||
on, and the register is not part of <CODE>contextreg</CODE>. Changes are sent to the target
|
||||
if the trace is live and "at the present." Otherwise, the change is materialized via
|
||||
emulation. Values changed by the last event are displayed in <FONT color=
|
||||
"red">red</FONT>.</LI>
|
||||
|
||||
<LI>Type - the type of the register as marked up in the trace. There is generally no default
|
||||
@@ -91,10 +92,15 @@
|
||||
|
||||
<H3><A name="enable_edits"></A>Enable Edits</H3>
|
||||
|
||||
<P>This toggle is a write protector for live registers. To modify live register values, this
|
||||
toggle must be enabled, and the trace must be live and "at the present." Note that editing
|
||||
recorded historical values is not permitted, regardless of this toggle, but can be accomplished
|
||||
via watches or scripts.</P>
|
||||
<P>This toggle is a write protector for target machine state. To modify register values, this
|
||||
toggle must be enabled. Editing a value when the trace is live and "at the present" will cause
|
||||
the value to be modified on the target. Editing emulated values is permitted, but ity has no
|
||||
effect on the target. Editing historical values is not permitted. All edits to non-live trace
|
||||
values are performed in emulation. Specifically, it appends a patch command to the current
|
||||
emulation schedule. This keeps trace history in tact, and it allows patched emulated states to
|
||||
be annotated and recalled later, since they are stored in the trace's scratch space. Note that
|
||||
only the raw "Value" column can be edited directory. The "Repr" column cannot be edited,
|
||||
yet.</P>
|
||||
|
||||
<H3><A name="snapshot_window"></A>Snapshot Window</H3>
|
||||
|
||||
|
||||
+34
-3
@@ -109,14 +109,14 @@
|
||||
trace forward to the next snapshot, causing most windows to display the recorded data from the
|
||||
new point in time.</P>
|
||||
|
||||
<H3><A name="step_trace_tick_backward"></A><IMG alt="" src="images/stepback.png">Step Trace
|
||||
<H3><A name="emu_trace_tick_backward"></A><IMG alt="" src="images/stepback.png">Emulate Trace
|
||||
Tick Backward</H3>
|
||||
|
||||
<P>This action is available when the current point in time includes emulated steps. It steps
|
||||
the trace backward to the previous tick.</P>
|
||||
|
||||
<H3><A name="step_trace_tick_forward"></A><IMG alt="" src="images/stepinto.png">Step Trace Tick
|
||||
Forward</H3>
|
||||
<H3><A name="emu_trace_tick_forward"></A><IMG alt="" src="images/stepinto.png">Emulate Trace
|
||||
Tick Forward</H3>
|
||||
|
||||
<P>This action is available when a thread is selected. It steps the current thread forward to
|
||||
the next tick, using emulation. Note that emulation does not affect the target. Furthermore,
|
||||
@@ -132,6 +132,37 @@
|
||||
not cause any windows to update. Toggling it off then on is a quick way to return to the
|
||||
present after browsing the past.</P>
|
||||
|
||||
<H3><A name="goto_time"></A>Go To Time</H3>
|
||||
|
||||
<P>This action is available when a trace is active. It prompts for a <B>Time Schedule</B>
|
||||
expression. This is the same form as the expression in the title bar of the threads window. In
|
||||
many cases, it is simply the snapshot number, e.g., <CODE>3</CODE>, which will go to the
|
||||
snapshot with key 3. It may optionally include an emulation schedule, for example,
|
||||
<CODE>3:10</CODE> will use snapshot 3 for an emulator's initial state and step 10 machine
|
||||
instructions on snapshot 3's event thread. If the snapshot does not give an event thread, then
|
||||
the thread must be specified in the expression, e.g., <CODE>3:t1-10</CODE>. That expression
|
||||
will start at snapshot 3, get the thread with key 1, and step it 10 machine instructions. The
|
||||
stepping commands can be repeated any number of times, separated by semicolons, to step threads
|
||||
in a specified sequence, e.g., <CODE>3:t1-10;t2-5</CODE> will do the same as before, then get
|
||||
thread 2 and step it 5 times.</P>
|
||||
|
||||
<P>The emulator's state can also be modified by the schedule. Instead of specifying a number of
|
||||
steps, write a <B>Sleigh</B> statement, e.g., <CODE>3:t1-{r0=0x1234};10</CODE>. This will start
|
||||
at snapshot 3, patch thread 1's r0 to 0x1234, then step 10 instructions. Like stepping
|
||||
commands, the thread may be omitted for Sleigh commands. Each command without a thread
|
||||
specified implicitly uses the one from the previous command, or in the case of the first
|
||||
command, the event thread. Only one Sleigh statement is permitted per command.</P>
|
||||
|
||||
<P>A second command sequence may be appended, following a dot, to command the emulator at the
|
||||
level of p-code operations as well. This is particularly useful when debugging a processor
|
||||
specification. See also the <A href=
|
||||
"help/topics/DebuggerPcodeStepperPlugin/DebuggerPcodeStepperPlugin.html">P-code Stepper</A>
|
||||
window. For example, <CODE>3:2.10</CODE> will start at snapshot 3 and step the event thread 2
|
||||
machine instructions, followed by 10 p-code operations. The same thread-by-thread sequencing
|
||||
and state patching commands are allowed in the p-code command sequence. The <EM>entire</EM>
|
||||
instruction sequence precedes the entire p-code sequence, i.e., only a single dot is allowed.
|
||||
Once the expression enters p-code mode, it cannot re-enter instruction mode.</P>
|
||||
|
||||
<H2>Other Actions</H2>
|
||||
|
||||
<H3><A name="sync_focus"></A>Synchronize Trace and Target Focus</H3>
|
||||
|
||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
+13
-9
@@ -62,9 +62,9 @@
|
||||
|
||||
<LI>Value - the raw bytes of the watched buffer. If the expression is a register, then this
|
||||
is its hexadecimal value. This field is user modifiable when the <B>Enable Edits</B> toggle
|
||||
is on. Changes are sent to the target if the trace is live and "at the present." If the value
|
||||
has changed since the last navigation event, this cell is rendered in <FONT color=
|
||||
"red">red</FONT>.</LI>
|
||||
is on. Changes are sent to the target if the trace is live and "at the present." Otherwise,
|
||||
the change is materialized via emulation. If the value has changed since the last navigation
|
||||
event, this cell is rendered in <FONT color="red">red</FONT>.</LI>
|
||||
|
||||
<LI>Type - the user-modifiable type of the watch. Note the type is not marked up in the
|
||||
trace. Clicking the Apply Data Type action will apply it to the current trace, if
|
||||
@@ -79,7 +79,7 @@
|
||||
not necessarily cause alarm. An expression devised for one context may not have meaning under
|
||||
another, even if it evaluates without error. E.g., <CODE>RIP</CODE> will disappear when
|
||||
switching to a 32-bit trace, or <CODE>*:8 (*:8 (RSP+8))</CODE> may cause an invalid
|
||||
dereference if an x86 <CODE>PUSH</CODE> causes <CODE>*:8 (RSP+8)</CODE> to become 0.</LI>
|
||||
dereference when the stack pointer moves.</LI>
|
||||
</UL>
|
||||
|
||||
<H2>Actions</H2>
|
||||
@@ -123,11 +123,15 @@
|
||||
|
||||
<H3><A name="enable_edits"></A>Enable Edits</H3>
|
||||
|
||||
<P>This toggle is a write protector for recorded and/or live values. To modify a watch's value,
|
||||
this toggle must be enabled. Editing a value when the trace is live and "at the present" will
|
||||
cause the value to be modified on the target. Editing historical and/or emulated values is
|
||||
permitted, but it has no effect on the target. Note that only the raw "Value" column can be
|
||||
edited directly. The "Repr" column cannot be edited, yet.</P>
|
||||
<P>This toggle is a write protector for target machine state. To modify a watch's value, this
|
||||
toggle must be enabled. Editing a value when the trace is live and "at the present" will cause
|
||||
the value to be modified on the target. Editing emulated values is permitted, but it has no
|
||||
effect on the target. Editing historical values is not permitted. All edits to non-live trace
|
||||
values are performed in emulation. Specifically, it appends a patch command to the current
|
||||
emulation schedule. This keeps trace history in tact, and it allows patched emulated states to
|
||||
be annotated and recalled later, since they are stored in the trace's scratch space. Note that
|
||||
only the raw "Value" column can be edited directly. The "Repr" column cannot be edited,
|
||||
yet.</P>
|
||||
|
||||
<H2><A name="colors"></A>Tool Options: Colors</H2>
|
||||
|
||||
|
||||
+5
-3
@@ -31,8 +31,8 @@ import ghidra.trace.database.DBTraceContentHandler;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.program.TraceProgramView;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.model.time.TraceSchedule;
|
||||
import ghidra.trace.model.time.TraceSnapshot;
|
||||
import ghidra.trace.model.time.schedule.TraceSchedule;
|
||||
import ghidra.trace.util.DefaultTraceTimeViewport;
|
||||
import ghidra.trace.util.TraceTimeViewport;
|
||||
import ghidra.util.Msg;
|
||||
@@ -259,8 +259,10 @@ public class DebuggerCoordinates {
|
||||
Collection<? extends TraceSnapshot> snapshots =
|
||||
trace.getTimeManager().getSnapshotsWithSchedule(time);
|
||||
if (snapshots.isEmpty()) {
|
||||
Msg.warn(this, "Seems the emulation service did not create the requested snapshot");
|
||||
return viewSnap = time.getSnap();
|
||||
Msg.warn(this,
|
||||
"Seems the emulation service did not create the requested snapshot, yet");
|
||||
// NB. Don't cache viewSnap. Maybe next time, we'll get it.
|
||||
return time.getSnap();
|
||||
}
|
||||
return viewSnap = snapshots.iterator().next().getKey();
|
||||
}
|
||||
|
||||
+50
-19
@@ -1520,24 +1520,24 @@ public interface DebuggerResources {
|
||||
}
|
||||
}
|
||||
|
||||
abstract class AbstractStepTickForwardAction extends DockingAction {
|
||||
public static final String NAME = "Step Trace Tick Forward";
|
||||
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 = "step_trace_tick_forward";
|
||||
public static final String HELP_ANCHOR = "emu_trace_tick_forward";
|
||||
|
||||
public AbstractStepTickForwardAction(Plugin owner) {
|
||||
public AbstractEmulateTickForwardAction(Plugin owner) {
|
||||
super(NAME, owner.getName());
|
||||
setDescription("Navigate the recording forward one tick");
|
||||
setDescription("Emulate the recording forward one tick");
|
||||
setHelpLocation(new HelpLocation(owner.getName(), HELP_ANCHOR));
|
||||
}
|
||||
}
|
||||
|
||||
interface StepPcodeForwardAction {
|
||||
String NAME = "Step Trace p-code Forward";
|
||||
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 = "step_trace_pcode_forward";
|
||||
String HELP_ANCHOR = "emu_trace_pcode_forward";
|
||||
|
||||
static ActionBuilder builder(Plugin owner) {
|
||||
String ownerName = owner.getName();
|
||||
@@ -1549,14 +1549,14 @@ public interface DebuggerResources {
|
||||
}
|
||||
}
|
||||
|
||||
abstract class AbstractStepTickBackwardAction extends DockingAction {
|
||||
public static final String NAME = "Step Trace Tick Backward";
|
||||
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 = "step_trace_tick_backward";
|
||||
public static final String HELP_ANCHOR = "emu_trace_tick_backward";
|
||||
|
||||
public AbstractStepTickBackwardAction(Plugin owner) {
|
||||
public AbstractEmulateTickBackwardAction(Plugin owner) {
|
||||
super(NAME, owner.getName());
|
||||
setDescription("Navigate the recording backward one tick");
|
||||
setDescription("Emulate the recording backward one tick");
|
||||
setHelpLocation(new HelpLocation(owner.getName(), HELP_ANCHOR));
|
||||
}
|
||||
}
|
||||
@@ -1573,12 +1573,12 @@ public interface DebuggerResources {
|
||||
}
|
||||
}
|
||||
|
||||
interface StepPcodeBackwardAction {
|
||||
String NAME = "Step Trace p-code Backward";
|
||||
interface EmulatePcodeBackwardAction {
|
||||
String NAME = "Emulate Trace p-code Backward";
|
||||
String DESCRIPTION = "Navigate the recording backward one p-code tick";
|
||||
Icon ICON = ICON_STEP_BACK;
|
||||
String GROUP = GROUP_CONTROL;
|
||||
String HELP_ANCHOR = "step_trace_pcode_backward";
|
||||
String HELP_ANCHOR = "emu_trace_pcode_backward";
|
||||
|
||||
static ActionBuilder builder(Plugin owner) {
|
||||
String ownerName = owner.getName();
|
||||
@@ -1605,15 +1605,30 @@ public interface DebuggerResources {
|
||||
interface SynchronizeFocusAction {
|
||||
String NAME = "Synchronize Focus";
|
||||
String DESCRIPTION = "Synchronize trace activation with debugger focus/select";
|
||||
String GROUP = "zz";
|
||||
Icon ICON = ICON_SYNC;
|
||||
String HELP_ANCHOR = "sync_focus";
|
||||
|
||||
static ToggleActionBuilder builder(Plugin owner) {
|
||||
String ownerName = owner.getName();
|
||||
return new ToggleActionBuilder(NAME, ownerName).description(DESCRIPTION)
|
||||
.toolBarGroup(GROUP)
|
||||
.toolBarIcon(ICON)
|
||||
.menuPath(NAME)
|
||||
.menuIcon(ICON)
|
||||
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
|
||||
}
|
||||
}
|
||||
|
||||
interface GoToTimeAction {
|
||||
String NAME = "Go To Time";
|
||||
String DESCRIPTION = "Go to a specific time, optionally using emulation";
|
||||
Icon ICON = ICON_TIME;
|
||||
String HELP_ANCHOR = "goto_time";
|
||||
|
||||
static ActionBuilder builder(Plugin owner) {
|
||||
String ownerName = owner.getName();
|
||||
return new ActionBuilder(NAME, ownerName).description(DESCRIPTION)
|
||||
.menuPath(NAME)
|
||||
.menuIcon(ICON)
|
||||
.keyBinding("CTRL SHIFT T")
|
||||
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
|
||||
}
|
||||
}
|
||||
@@ -1808,6 +1823,22 @@ public interface DebuggerResources {
|
||||
}
|
||||
}
|
||||
|
||||
interface ForceFullViewAction {
|
||||
String NAME = "Force Full View";
|
||||
String DESCRIPTION = "Ignore regions and fiew full address spaces";
|
||||
String GROUP = GROUP_GENERAL;
|
||||
String HELP_ANCHOR = "force_full_view";
|
||||
|
||||
static ToggleActionBuilder builder(Plugin owner) {
|
||||
String ownerName = owner.getName();
|
||||
return new ToggleActionBuilder(NAME, ownerName)
|
||||
.description(DESCRIPTION)
|
||||
.menuGroup(GROUP_GENERAL)
|
||||
.menuPath(NAME)
|
||||
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class AbstractDebuggerConnectionsNode extends GTreeNode {
|
||||
@Override
|
||||
public String getName() {
|
||||
|
||||
+8
-2
@@ -46,8 +46,14 @@ public class DebuggerMemoryByteViewerComponent extends ByteViewerComponent
|
||||
List<ColoredFieldSelection> selections) {
|
||||
Color selectionColor = paintContext.getSelectionColor();
|
||||
Color highlightColor = paintContext.getHighlightColor();
|
||||
selections.add(new ColoredFieldSelection(getSelection(), selectionColor));
|
||||
selections.add(new ColoredFieldSelection(getHighlight(), highlightColor));
|
||||
FieldSelection selection = getSelection();
|
||||
if (!selection.isEmpty()) {
|
||||
selections.add(new ColoredFieldSelection(selection, selectionColor));
|
||||
}
|
||||
FieldSelection highlight = getHighlight();
|
||||
if (!highlight.isEmpty()) {
|
||||
selections.add(new ColoredFieldSelection(highlight, highlightColor));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+24
-1
@@ -253,6 +253,7 @@ public class DebuggerRegionsProvider extends ComponentProviderAdapter {
|
||||
|
||||
SelectAddressesAction actionSelectAddresses;
|
||||
DockingAction actionSelectRows;
|
||||
ToggleDockingAction actionForceFullView;
|
||||
|
||||
public DebuggerRegionsProvider(DebuggerRegionsPlugin plugin) {
|
||||
super(plugin.getTool(), DebuggerResources.TITLE_PROVIDER_REGIONS, plugin.getName(),
|
||||
@@ -382,7 +383,10 @@ public class DebuggerRegionsProvider extends ComponentProviderAdapter {
|
||||
.enabledWhen(ctx -> currentTrace != null)
|
||||
.onAction(this::activatedSelectCurrent)
|
||||
.buildAndInstallLocal(this);
|
||||
|
||||
actionForceFullView = ForceFullViewAction.builder(plugin)
|
||||
.enabledWhen(ctx -> currentTrace != null)
|
||||
.onAction(this::activatedForceFullView)
|
||||
.buildAndInstallLocal(this);
|
||||
contextChanged();
|
||||
}
|
||||
|
||||
@@ -501,6 +505,15 @@ public class DebuggerRegionsProvider extends ComponentProviderAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
private void activatedForceFullView(ActionContext ignored) {
|
||||
if (currentTrace == null) {
|
||||
return;
|
||||
}
|
||||
currentTrace.getProgramView()
|
||||
.getMemory()
|
||||
.setForceFullView(actionForceFullView.isSelected());
|
||||
}
|
||||
|
||||
public void setSelectedRegions(Set<TraceMemoryRegion> sel) {
|
||||
DebuggerResources.setSelectedRows(sel, regionTableModel::getRow, regionTable,
|
||||
regionTableModel, regionFilterPanel);
|
||||
@@ -554,6 +567,16 @@ public class DebuggerRegionsProvider extends ComponentProviderAdapter {
|
||||
contextChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contextChanged() {
|
||||
super.contextChanged();
|
||||
if (currentTrace != null) {
|
||||
actionForceFullView.setSelected(currentTrace.getProgramView()
|
||||
.getMemory()
|
||||
.isForceFullView());
|
||||
}
|
||||
}
|
||||
|
||||
private void removeOldListeners() {
|
||||
if (currentTrace == null) {
|
||||
return;
|
||||
|
||||
+3
-3
@@ -55,7 +55,7 @@ import ghidra.program.model.lang.Language;
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.time.TraceSchedule;
|
||||
import ghidra.trace.model.time.schedule.TraceSchedule;
|
||||
import ghidra.util.ColorUtils;
|
||||
import ghidra.util.HTMLUtilities;
|
||||
import ghidra.util.database.UndoableTransaction;
|
||||
@@ -510,11 +510,11 @@ public class DebuggerPcodeStepperProvider extends ComponentProviderAdapter {
|
||||
}
|
||||
|
||||
protected void createActions() {
|
||||
actionStepBackward = DebuggerResources.StepPcodeBackwardAction.builder(plugin)
|
||||
actionStepBackward = DebuggerResources.EmulatePcodeBackwardAction.builder(plugin)
|
||||
.enabledWhen(c -> current.getTrace() != null && current.getTime().pTickCount() != 0)
|
||||
.onAction(c -> stepBackwardActivated())
|
||||
.buildAndInstallLocal(this);
|
||||
actionStepForward = DebuggerResources.StepPcodeForwardAction.builder(plugin)
|
||||
actionStepForward = DebuggerResources.EmulatePcodeForwardAction.builder(plugin)
|
||||
.enabledWhen(
|
||||
c -> current.getThread() != null)
|
||||
.onAction(c -> stepForwardActivated())
|
||||
|
||||
+43
-23
@@ -72,6 +72,7 @@ import ghidra.trace.model.memory.TraceMemoryRegisterSpace;
|
||||
import ghidra.trace.model.memory.TraceMemoryState;
|
||||
import ghidra.trace.model.program.TraceProgramView;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.model.time.schedule.TraceSchedule;
|
||||
import ghidra.trace.util.*;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.Swing;
|
||||
@@ -755,8 +756,21 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean canWriteRegister(Register register) {
|
||||
if (!isEditsEnabled()) {
|
||||
return false;
|
||||
}
|
||||
if (register.isProcessorContext()) {
|
||||
return false; // TODO: Limitation from using Sleigh for patching
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean canWriteTargetRegister(Register register) {
|
||||
if (!computeEditsEnabled()) {
|
||||
if (!isEditsEnabled()) {
|
||||
return false;
|
||||
}
|
||||
if (!canWriteTarget()) {
|
||||
return false;
|
||||
}
|
||||
return current.getRecorder().isRegisterOnTarget(current.getThread(), register);
|
||||
@@ -775,23 +789,32 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
|
||||
}
|
||||
|
||||
void writeRegisterValue(RegisterValue rv) {
|
||||
rv = combineWithTraceBaseRegisterValue(rv);
|
||||
CompletableFuture<Void> future = current.getRecorder()
|
||||
.writeThreadRegisters(current.getThread(), current.getFrame(),
|
||||
Map.of(rv.getRegister(), rv));
|
||||
future.exceptionally(ex -> {
|
||||
ex = AsyncUtils.unwrapThrowable(ex);
|
||||
if (ex instanceof DebuggerModelAccessException) {
|
||||
Msg.error(this, "Could not write target register", ex);
|
||||
plugin.getTool()
|
||||
.setStatusInfo("Could not write target register: " + ex.getMessage());
|
||||
}
|
||||
else {
|
||||
Msg.showError(this, getComponent(), "Edit Register",
|
||||
"Could not write target register", ex);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
if (canWriteTargetRegister(rv.getRegister())) {
|
||||
rv = combineWithTraceBaseRegisterValue(rv);
|
||||
CompletableFuture<Void> future = current.getRecorder()
|
||||
.writeThreadRegisters(current.getThread(), current.getFrame(),
|
||||
Map.of(rv.getRegister(), rv));
|
||||
future.exceptionally(ex -> {
|
||||
ex = AsyncUtils.unwrapThrowable(ex);
|
||||
if (ex instanceof DebuggerModelAccessException) {
|
||||
Msg.error(this, "Could not write target register", ex);
|
||||
plugin.getTool()
|
||||
.setStatusInfo("Could not write target register: " + ex.getMessage());
|
||||
}
|
||||
else {
|
||||
Msg.showError(this, getComponent(), "Edit Register",
|
||||
"Could not write target register", ex);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
return;
|
||||
}
|
||||
TraceSchedule time = current.getTime().patched(current.getThread(), generateSleigh(rv));
|
||||
traceManager.activateTime(time);
|
||||
}
|
||||
|
||||
protected String generateSleigh(RegisterValue rv) {
|
||||
return String.format("%s=0x%s", rv.getRegister(), rv.getUnsignedValue().toString(16));
|
||||
}
|
||||
|
||||
private RegisterValue combineWithTraceBaseRegisterValue(RegisterValue rv) {
|
||||
@@ -886,11 +909,8 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
|
||||
return !Objects.equals(curRegVal, prevRegVal);
|
||||
}
|
||||
|
||||
private boolean computeEditsEnabled() {
|
||||
if (!actionEnableEdits.isSelected()) {
|
||||
return false;
|
||||
}
|
||||
return canWriteTarget();
|
||||
private boolean isEditsEnabled() {
|
||||
return actionEnableEdits.isSelected();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+1
-1
@@ -62,7 +62,7 @@ public class RegisterRow {
|
||||
}
|
||||
|
||||
public boolean isValueEditable() {
|
||||
return provider.canWriteTargetRegister(register);
|
||||
return provider.canWriteRegister(register);
|
||||
}
|
||||
|
||||
public void setValue(BigInteger value) {
|
||||
|
||||
+32
-9
@@ -33,6 +33,7 @@ import docking.WindowPosition;
|
||||
import docking.action.*;
|
||||
import docking.widgets.HorizontalTabPanel;
|
||||
import docking.widgets.HorizontalTabPanel.TabListCellRenderer;
|
||||
import docking.widgets.dialogs.InputDialog;
|
||||
import docking.widgets.table.*;
|
||||
import ghidra.app.plugin.core.debug.DebuggerCoordinates;
|
||||
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
|
||||
@@ -53,8 +54,9 @@ import ghidra.trace.model.Trace.TraceThreadChangeType;
|
||||
import ghidra.trace.model.TraceDomainObjectListener;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.model.thread.TraceThreadManager;
|
||||
import ghidra.trace.model.time.TraceSchedule;
|
||||
import ghidra.trace.model.time.TraceSnapshot;
|
||||
import ghidra.trace.model.time.schedule.TraceSchedule;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.Swing;
|
||||
import ghidra.util.database.ObjectKey;
|
||||
import ghidra.util.datastruct.CollectionChangeListener;
|
||||
@@ -120,10 +122,10 @@ public class DebuggerThreadsProvider extends ComponentProviderAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
protected class StepTickBackwardAction extends AbstractStepTickBackwardAction {
|
||||
protected class EmulateTickBackwardAction extends AbstractEmulateTickBackwardAction {
|
||||
public static final String GROUP = DebuggerResources.GROUP_CONTROL;
|
||||
|
||||
public StepTickBackwardAction() {
|
||||
public EmulateTickBackwardAction() {
|
||||
super(plugin);
|
||||
setToolBarData(new ToolBarData(ICON, GROUP, "2"));
|
||||
addLocalAction(this);
|
||||
@@ -157,10 +159,10 @@ public class DebuggerThreadsProvider extends ComponentProviderAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
protected class StepTickForwardAction extends AbstractStepTickForwardAction {
|
||||
protected class EmulateTickForwardAction extends AbstractEmulateTickForwardAction {
|
||||
public static final String GROUP = DebuggerResources.GROUP_CONTROL;
|
||||
|
||||
public StepTickForwardAction() {
|
||||
public EmulateTickForwardAction() {
|
||||
super(plugin);
|
||||
setToolBarData(new ToolBarData(ICON, GROUP, "3"));
|
||||
addLocalAction(this);
|
||||
@@ -353,11 +355,12 @@ public class DebuggerThreadsProvider extends ComponentProviderAdapter {
|
||||
|
||||
DockingAction actionSaveTrace;
|
||||
StepSnapBackwardAction actionStepSnapBackward;
|
||||
StepTickBackwardAction actionStepTickBackward;
|
||||
StepTickForwardAction actionStepTickForward;
|
||||
EmulateTickBackwardAction actionEmulateTickBackward;
|
||||
EmulateTickForwardAction actionEmulateTickForward;
|
||||
StepSnapForwardAction actionStepSnapForward;
|
||||
SeekTracePresentAction actionSeekTracePresent;
|
||||
ToggleDockingAction actionSyncFocus;
|
||||
DockingAction actionGoToTime;
|
||||
Set<Object> strongRefs = new HashSet<>(); // Eww
|
||||
|
||||
public DebuggerThreadsProvider(final DebuggerThreadsPlugin plugin) {
|
||||
@@ -669,8 +672,8 @@ public class DebuggerThreadsProvider extends ComponentProviderAdapter {
|
||||
protected void createActions() {
|
||||
// TODO: Make other actions use builder?
|
||||
actionStepSnapBackward = new StepSnapBackwardAction();
|
||||
actionStepTickBackward = new StepTickBackwardAction();
|
||||
actionStepTickForward = new StepTickForwardAction();
|
||||
actionEmulateTickBackward = new EmulateTickBackwardAction();
|
||||
actionEmulateTickForward = new EmulateTickForwardAction();
|
||||
actionStepSnapForward = new StepSnapForwardAction();
|
||||
actionSeekTracePresent = new SeekTracePresentAction();
|
||||
actionSyncFocus = SynchronizeFocusAction.builder(plugin)
|
||||
@@ -678,6 +681,10 @@ public class DebuggerThreadsProvider extends ComponentProviderAdapter {
|
||||
.enabledWhen(c -> traceManager != null)
|
||||
.onAction(c -> toggleSyncFocus(actionSyncFocus.isSelected()))
|
||||
.buildAndInstallLocal(this);
|
||||
actionGoToTime = GoToTimeAction.builder(plugin)
|
||||
.enabledWhen(c -> current.getTrace() != null)
|
||||
.onAction(c -> activatedGoToTime())
|
||||
.buildAndInstallLocal(this);
|
||||
traceManager.addSynchronizeFocusChangeListener(
|
||||
strongRef(new ToToggleSelectionListener(actionSyncFocus)));
|
||||
}
|
||||
@@ -689,6 +696,22 @@ public class DebuggerThreadsProvider extends ComponentProviderAdapter {
|
||||
traceManager.setSynchronizeFocus(enabled);
|
||||
}
|
||||
|
||||
private void activatedGoToTime() {
|
||||
InputDialog dialog =
|
||||
new InputDialog("Go To Time", "Schedule:", current.getTime().toString());
|
||||
tool.showDialog(dialog);
|
||||
if (dialog.isCanceled()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
TraceSchedule time = TraceSchedule.parse(dialog.getValue());
|
||||
traceManager.activateTime(time);
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
Msg.showError(this, getComponent(), "Go To Time", "Could not parse schedule");
|
||||
}
|
||||
}
|
||||
|
||||
private void traceTabSelected(ListSelectionEvent e) {
|
||||
if (e.getValueIsAdjusting()) {
|
||||
return;
|
||||
|
||||
+12
-13
@@ -24,19 +24,18 @@ import ghidra.framework.options.SaveState;
|
||||
import ghidra.framework.plugintool.*;
|
||||
import ghidra.framework.plugintool.util.PluginStatus;
|
||||
|
||||
@PluginInfo( //
|
||||
shortDescription = "Lists recorded snapshots in a trace", //
|
||||
description = "Provides the component which lists snapshots and allows navigation", //
|
||||
category = PluginCategoryNames.DEBUGGER, //
|
||||
packageName = DebuggerPluginPackage.NAME, //
|
||||
status = PluginStatus.RELEASED, //
|
||||
eventsConsumed = { //
|
||||
TraceActivatedPluginEvent.class //
|
||||
}, //
|
||||
servicesRequired = { //
|
||||
DebuggerTraceManagerService.class //
|
||||
} //
|
||||
)
|
||||
@PluginInfo(
|
||||
shortDescription = "Lists recorded snapshots in a trace",
|
||||
description = "Provides the component which lists snapshots and allows navigation",
|
||||
category = PluginCategoryNames.DEBUGGER,
|
||||
packageName = DebuggerPluginPackage.NAME,
|
||||
status = PluginStatus.RELEASED,
|
||||
eventsConsumed = {
|
||||
TraceActivatedPluginEvent.class
|
||||
},
|
||||
servicesRequired = {
|
||||
DebuggerTraceManagerService.class
|
||||
})
|
||||
public class DebuggerTimePlugin extends AbstractDebuggerPlugin {
|
||||
protected DebuggerTimeProvider provider;
|
||||
|
||||
|
||||
+9
-1
@@ -38,6 +38,7 @@ import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
|
||||
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
|
||||
import ghidra.app.plugin.core.debug.gui.DebuggerResources.*;
|
||||
import ghidra.app.services.DebuggerListingService;
|
||||
import ghidra.app.services.DebuggerTraceManagerService;
|
||||
import ghidra.async.AsyncDebouncer;
|
||||
import ghidra.async.AsyncTimer;
|
||||
import ghidra.base.widgets.table.DataTypeTableCellEditor;
|
||||
@@ -60,6 +61,7 @@ import ghidra.program.util.ProgramSelection;
|
||||
import ghidra.trace.model.*;
|
||||
import ghidra.trace.model.Trace.TraceMemoryBytesChangeType;
|
||||
import ghidra.trace.model.Trace.TraceMemoryStateChangeType;
|
||||
import ghidra.trace.model.time.schedule.TraceSchedule;
|
||||
import ghidra.trace.util.TraceAddressSpace;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.Swing;
|
||||
@@ -237,8 +239,10 @@ public class DebuggerWatchesProvider extends ComponentProviderAdapter {
|
||||
private Trace currentTrace; // Copy for transition
|
||||
|
||||
@AutoServiceConsumed
|
||||
private DebuggerListingService listingService; // TODO: For goto and selection
|
||||
private DebuggerListingService listingService; // For goto and selection
|
||||
// TODO: Allow address marking
|
||||
@AutoServiceConsumed
|
||||
private DebuggerTraceManagerService traceManager; // For goto time (emu mods)
|
||||
@SuppressWarnings("unused")
|
||||
private final AutoService.Wiring autoServiceWiring;
|
||||
|
||||
@@ -639,4 +643,8 @@ public class DebuggerWatchesProvider extends ComponentProviderAdapter {
|
||||
public boolean isEditsEnabled() {
|
||||
return actionEnableEdits.isSelected();
|
||||
}
|
||||
|
||||
public void goToTime(TraceSchedule time) {
|
||||
traceManager.activateTime(time);
|
||||
}
|
||||
}
|
||||
|
||||
+23
-3
@@ -16,7 +16,6 @@
|
||||
package ghidra.app.plugin.core.debug.gui.watch;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
@@ -33,14 +32,15 @@ import ghidra.pcode.utils.Utils;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.program.model.mem.ByteMemBufferImpl;
|
||||
import ghidra.program.model.mem.MemBuffer;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.memory.TraceMemorySpace;
|
||||
import ghidra.trace.model.memory.TraceMemoryState;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.model.time.schedule.TraceSchedule;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.database.UndoableTransaction;
|
||||
|
||||
public class WatchRow {
|
||||
public static final int TRUNCATE_BYTES_LENGTH = 64;
|
||||
@@ -415,7 +415,7 @@ public class WatchRow {
|
||||
return;
|
||||
}
|
||||
|
||||
try (UndoableTransaction tid =
|
||||
/*try (UndoableTransaction tid =
|
||||
UndoableTransaction.start(trace, "Write watch at " + address, true)) {
|
||||
final TraceMemorySpace space;
|
||||
if (address.isRegisterAddress()) {
|
||||
@@ -427,7 +427,27 @@ public class WatchRow {
|
||||
space = trace.getMemoryManager().getMemorySpace(address.getAddressSpace(), true);
|
||||
}
|
||||
space.putBytes(coordinates.getViewSnap(), address, ByteBuffer.wrap(bytes));
|
||||
}*/
|
||||
TraceSchedule time =
|
||||
coordinates.getTime().patched(coordinates.getThread(), generateSleigh(bytes));
|
||||
provider.goToTime(time);
|
||||
}
|
||||
|
||||
protected String generateSleigh(byte[] bytes) {
|
||||
BigInteger value = Utils.bytesToBigInteger(bytes, bytes.length,
|
||||
trace.getBaseLanguage().isBigEndian(), false);
|
||||
if (address.isMemoryAddress()) {
|
||||
AddressSpace space = address.getAddressSpace();
|
||||
return String.format("*[%s]:%d 0x%s:%d=0x%s",
|
||||
space.getName(), bytes.length,
|
||||
address.getOffsetAsBigInteger().toString(16), space.getPointerSize(),
|
||||
value.toString(16));
|
||||
}
|
||||
Register register = trace.getBaseLanguage().getRegister(address, bytes.length);
|
||||
if (register == null) {
|
||||
throw new AssertionError("Can only modify memory or register");
|
||||
}
|
||||
return String.format("%s=0x%s", register, value.toString(16));
|
||||
}
|
||||
|
||||
public int getValueLength() {
|
||||
|
||||
+1
-1
@@ -57,7 +57,7 @@ public abstract class AbstractReadsTargetPcodeExecutorState
|
||||
public byte[] read(long offset, int size) {
|
||||
if (source != null) {
|
||||
AddressSet uninitialized = new AddressSet();
|
||||
for (Range<UnsignedLong> rng : cache.getUninitialized(offset, offset + size)
|
||||
for (Range<UnsignedLong> rng : cache.getUninitialized(offset, offset + size - 1)
|
||||
.asRanges()) {
|
||||
uninitialized.add(space.getAddress(lower(rng)),
|
||||
space.getAddress(upper(rng)));
|
||||
|
||||
+2
-2
@@ -45,9 +45,9 @@ import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.trace.model.*;
|
||||
import ghidra.trace.model.program.TraceProgramView;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.model.time.TraceSchedule;
|
||||
import ghidra.trace.model.time.TraceSchedule.CompareResult;
|
||||
import ghidra.trace.model.time.TraceSnapshot;
|
||||
import ghidra.trace.model.time.schedule.CompareResult;
|
||||
import ghidra.trace.model.time.schedule.TraceSchedule;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.database.UndoableTransaction;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
|
||||
+6
-2
@@ -53,8 +53,8 @@ import ghidra.trace.model.program.TraceProgramView;
|
||||
import ghidra.trace.model.program.TraceVariableSnapProgramView;
|
||||
import ghidra.trace.model.stack.TraceStackFrame;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.model.time.TraceSchedule;
|
||||
import ghidra.trace.model.time.TraceSnapshot;
|
||||
import ghidra.trace.model.time.schedule.TraceSchedule;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.datastruct.CollectionChangeListener;
|
||||
import ghidra.util.exception.*;
|
||||
@@ -693,7 +693,11 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
|
||||
}
|
||||
varView.setSnap(emuSnap);
|
||||
fireLocationEvent(coordinates);
|
||||
}));
|
||||
})).exceptionally(ex -> {
|
||||
Msg.showError(this, null, "Emulate", "Could not navigate to emulated coordinates",
|
||||
ex);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ import ghidra.app.plugin.core.debug.service.emulation.DebuggerEmulationServicePl
|
||||
import ghidra.app.plugin.core.debug.service.emulation.DebuggerTracePcodeEmulator;
|
||||
import ghidra.framework.plugintool.ServiceInfo;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.time.TraceSchedule;
|
||||
import ghidra.trace.model.time.schedule.TraceSchedule;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ import ghidra.framework.plugintool.ServiceInfo;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.program.TraceProgramView;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.model.time.TraceSchedule;
|
||||
import ghidra.trace.model.time.schedule.TraceSchedule;
|
||||
import ghidra.util.TriConsumer;
|
||||
|
||||
@ServiceInfo(defaultProvider = DebuggerTraceManagerServicePlugin.class)
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ import ghidra.test.ToyProgramBuilder;
|
||||
import ghidra.trace.database.ToyDBTraceBuilder;
|
||||
import ghidra.trace.model.memory.TraceMemoryFlag;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.model.time.TraceSchedule;
|
||||
import ghidra.trace.model.time.schedule.TraceSchedule;
|
||||
import ghidra.util.database.UndoableTransaction;
|
||||
import help.screenshot.GhidraScreenShotGenerator;
|
||||
|
||||
|
||||
+1
-1
@@ -22,8 +22,8 @@ import ghidra.app.services.DebuggerTraceManagerService;
|
||||
import ghidra.test.ToyProgramBuilder;
|
||||
import ghidra.trace.database.ToyDBTraceBuilder;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.model.time.TraceSchedule;
|
||||
import ghidra.trace.model.time.TraceSnapshot;
|
||||
import ghidra.trace.model.time.schedule.TraceSchedule;
|
||||
import ghidra.util.database.UndoableTransaction;
|
||||
import help.screenshot.GhidraScreenShotGenerator;
|
||||
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ import ghidra.program.model.listing.Instruction;
|
||||
import ghidra.program.model.listing.InstructionIterator;
|
||||
import ghidra.trace.model.memory.TraceMemoryFlag;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.model.time.TraceSchedule;
|
||||
import ghidra.trace.model.time.schedule.TraceSchedule;
|
||||
import ghidra.util.database.UndoableTransaction;
|
||||
|
||||
public class DebuggerPcodeStepperProviderTest extends AbstractGhidraHeadedDebuggerGUITest {
|
||||
|
||||
+34
@@ -59,6 +59,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
|
||||
protected Register r0;
|
||||
protected Register pc;
|
||||
protected Register sp;
|
||||
protected Register contextreg;
|
||||
|
||||
protected Register r0h;
|
||||
protected Register r0l;
|
||||
@@ -79,6 +80,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
|
||||
r0 = tb.language.getRegister("r0");
|
||||
pc = tb.language.getProgramCounter();
|
||||
sp = tb.language.getDefaultCompilerSpec().getStackPointer();
|
||||
contextreg = tb.language.getContextBaseRegister();
|
||||
|
||||
pch = tb.language.getRegister("pch");
|
||||
pcl = tb.language.getRegister("pcl");
|
||||
@@ -381,6 +383,38 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
|
||||
assertR0RowTypePopulated();
|
||||
}
|
||||
|
||||
// TODO: Test that contextreg cannot be modified
|
||||
// TODO: Make contextreg modifiable by Registers window
|
||||
|
||||
@Test
|
||||
public void testDeadModifyValueEmulates() throws Exception {
|
||||
traceManager.openTrace(tb.trace);
|
||||
|
||||
TraceThread thread = addThread();
|
||||
traceManager.activateThread(thread);
|
||||
waitForSwing();
|
||||
|
||||
assertTrue(registersProvider.actionEnableEdits.isEnabled());
|
||||
performAction(registersProvider.actionEnableEdits);
|
||||
|
||||
addRegisterValues(thread);
|
||||
waitForDomainObject(tb.trace);
|
||||
|
||||
TraceMemoryRegisterSpace regVals =
|
||||
tb.trace.getMemoryManager().getMemoryRegisterSpace(thread, false);
|
||||
|
||||
RegisterRow row = findRegisterRow(r0);
|
||||
|
||||
setRowText(row, "1234");
|
||||
waitForSwing();
|
||||
waitForPass(() -> {
|
||||
long viewSnap = traceManager.getCurrent().getViewSnap();
|
||||
assertEquals(BigInteger.valueOf(0x1234),
|
||||
regVals.getValue(viewSnap, r0).getUnsignedValue());
|
||||
assertEquals(BigInteger.valueOf(0x1234), row.getValue());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLiveModifyValueAffectsTarget() throws Exception {
|
||||
TraceRecorder recorder = recordAndWaitSync();
|
||||
|
||||
+3
-3
@@ -26,8 +26,8 @@ import org.junit.Test;
|
||||
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest;
|
||||
import ghidra.trace.database.time.DBTraceTimeManager;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.model.time.TraceSchedule;
|
||||
import ghidra.trace.model.time.TraceSnapshot;
|
||||
import ghidra.trace.model.time.schedule.TraceSchedule;
|
||||
import ghidra.util.database.UndoableTransaction;
|
||||
|
||||
public class DebuggerTimeProviderTest extends AbstractGhidraHeadedDebuggerGUITest {
|
||||
@@ -50,7 +50,7 @@ public class DebuggerTimeProviderTest extends AbstractGhidraHeadedDebuggerGUITes
|
||||
first.setRealTime(c.getTimeInMillis());
|
||||
TraceSnapshot second = timeManager.getSnapshot(10, true);
|
||||
second.setDescription("Snap 10");
|
||||
second.setSchedule(TraceSchedule.parse("0:5,t1-5"));
|
||||
second.setSchedule(TraceSchedule.parse("0:5;t1-5"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ public class DebuggerTimeProviderTest extends AbstractGhidraHeadedDebuggerGUITes
|
||||
SnapshotRow secondRow = snapsDisplayed.get(1);
|
||||
assertEquals(10, secondRow.getSnap());
|
||||
assertEquals("Snap 10", secondRow.getDescription());
|
||||
assertEquals("0:5,t1-5", secondRow.getSchedule());
|
||||
assertEquals("0:5;t1-5", secondRow.getSchedule());
|
||||
// Timestamp is left unchecked, since default is current time
|
||||
}
|
||||
|
||||
|
||||
+38
-27
@@ -292,39 +292,49 @@ public class DebuggerWatchesProviderTest extends AbstractGhidraHeadedDebuggerGUI
|
||||
@Test
|
||||
public void testDeadEditRegister() {
|
||||
WatchRow row = prepareTestDeadEdit("r0");
|
||||
|
||||
row.setRawValueString("0x1234");
|
||||
waitForSwing();
|
||||
|
||||
TraceMemoryRegisterSpace regVals =
|
||||
tb.trace.getMemoryManager().getMemoryRegisterSpace(thread, false);
|
||||
assertEquals(BigInteger.valueOf(0x1234), regVals.getValue(0, r0).getUnsignedValue());
|
||||
|
||||
row.setRawValueString("1234");
|
||||
waitForSwing();
|
||||
row.setRawValueString("0x1234");
|
||||
waitForPass(() -> {
|
||||
long viewSnap = traceManager.getCurrent().getViewSnap();
|
||||
assertEquals(BigInteger.valueOf(0x1234),
|
||||
regVals.getValue(viewSnap, r0).getUnsignedValue());
|
||||
assertEquals("0x1234", row.getRawValueString());
|
||||
});
|
||||
|
||||
assertEquals(BigInteger.valueOf(1234), regVals.getValue(0, r0).getUnsignedValue());
|
||||
row.setRawValueString("1234"); // Decimal this time
|
||||
waitForPass(() -> {
|
||||
long viewSnap = traceManager.getCurrent().getViewSnap();
|
||||
assertEquals(BigInteger.valueOf(1234),
|
||||
regVals.getValue(viewSnap, r0).getUnsignedValue());
|
||||
assertEquals("0x4d2", row.getRawValueString());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeadEditMemory() {
|
||||
WatchRow row = prepareTestDeadEdit("*:8 r0");
|
||||
|
||||
row.setRawValueString("0x1234");
|
||||
waitForSwing();
|
||||
|
||||
TraceMemoryOperations mem = tb.trace.getMemoryManager();
|
||||
ByteBuffer buf = ByteBuffer.allocate(8);
|
||||
mem.getBytes(0, tb.addr(0x00400000), buf);
|
||||
buf.flip();
|
||||
assertEquals(0x1234, buf.getLong());
|
||||
|
||||
row.setRawValueString("0x1234");
|
||||
waitForPass(() -> {
|
||||
long viewSnap = traceManager.getCurrent().getViewSnap();
|
||||
buf.clear();
|
||||
mem.getBytes(viewSnap, tb.addr(0x00400000), buf);
|
||||
buf.flip();
|
||||
assertEquals(0x1234, buf.getLong());
|
||||
});
|
||||
|
||||
row.setRawValueString("{ 12 34 56 78 9a bc de f0 }");
|
||||
waitForSwing();
|
||||
buf.clear();
|
||||
mem.getBytes(0, tb.addr(0x00400000), buf);
|
||||
buf.flip();
|
||||
assertEquals(0x123456789abcdef0L, buf.getLong());
|
||||
waitForPass(() -> {
|
||||
long viewSnap = traceManager.getCurrent().getViewSnap();
|
||||
buf.clear();
|
||||
mem.getBytes(viewSnap, tb.addr(0x00400000), buf);
|
||||
buf.flip();
|
||||
assertEquals(0x123456789abcdef0L, buf.getLong());
|
||||
});
|
||||
}
|
||||
|
||||
protected WatchRow prepareTestLiveEdit(String expression) throws Exception {
|
||||
@@ -379,17 +389,18 @@ public class DebuggerWatchesProviderTest extends AbstractGhidraHeadedDebuggerGUI
|
||||
public void testLiveEditNonMappableRegister() throws Throwable {
|
||||
WatchRow row = prepareTestLiveEdit("r1");
|
||||
TraceThread thread = recorder.getTraceThread(mb.testThread1);
|
||||
|
||||
// Sanity check
|
||||
assertFalse(recorder.isRegisterOnTarget(thread, r1));
|
||||
|
||||
row.setRawValueString("0x1234");
|
||||
waitForSwing();
|
||||
|
||||
TraceMemoryRegisterSpace regs =
|
||||
recorder.getTrace().getMemoryManager().getMemoryRegisterSpace(thread, false);
|
||||
assertEquals(BigInteger.valueOf(0x1234),
|
||||
regs.getValue(recorder.getSnap(), r1).getUnsignedValue());
|
||||
waitForPass(() -> {
|
||||
TraceMemoryRegisterSpace regs =
|
||||
recorder.getTrace().getMemoryManager().getMemoryRegisterSpace(thread, false);
|
||||
assertNotNull(regs);
|
||||
long viewSnap = traceManager.getCurrent().getViewSnap();
|
||||
assertEquals(BigInteger.valueOf(0x1234),
|
||||
regs.getValue(viewSnap, r1).getUnsignedValue());
|
||||
});
|
||||
|
||||
assertFalse(bank.regVals.containsKey("r1"));
|
||||
}
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ import ghidra.program.model.mem.MemoryBlock;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.memory.TraceMemoryRegisterSpace;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.model.time.TraceSchedule;
|
||||
import ghidra.trace.model.time.schedule.TraceSchedule;
|
||||
import ghidra.util.database.UndoableTransaction;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
|
||||
@@ -58,11 +58,29 @@ public class CachedMemory implements MemoryReader, MemoryWriter {
|
||||
private final MemoryWriter writer;
|
||||
|
||||
protected static class PendingRead {
|
||||
protected static Range<UnsignedLong> normalize(Range<UnsignedLong> range) {
|
||||
if (range.lowerBoundType() == BoundType.CLOSED) {
|
||||
if (range.upperBoundType() == BoundType.OPEN ||
|
||||
range.upperEndpoint().longValue() == -1) {
|
||||
return range;
|
||||
}
|
||||
return Range.closedOpen(range.lowerEndpoint(),
|
||||
range.upperEndpoint().plus(UnsignedLong.ONE));
|
||||
}
|
||||
assert range.lowerEndpoint().longValue() != -1;
|
||||
UnsignedLong lower = range.lowerEndpoint().plus(UnsignedLong.ONE);
|
||||
if (range.upperBoundType() == BoundType.OPEN ||
|
||||
range.upperEndpoint().longValue() == -1) {
|
||||
return Range.closed(lower, range.upperEndpoint());
|
||||
}
|
||||
return Range.closedOpen(lower, range.upperEndpoint().plus(UnsignedLong.ONE));
|
||||
}
|
||||
|
||||
final Range<UnsignedLong> range;
|
||||
final CompletableFuture<Void> future;
|
||||
|
||||
protected PendingRead(Range<UnsignedLong> range, CompletableFuture<Void> future) {
|
||||
this.range = range;
|
||||
this.range = normalize(range);
|
||||
this.future = future;
|
||||
}
|
||||
}
|
||||
@@ -90,7 +108,7 @@ public class CachedMemory implements MemoryReader, MemoryWriter {
|
||||
}
|
||||
|
||||
protected synchronized CompletableFuture<Void> waitForReads(long addr, int len) {
|
||||
RangeSet<UnsignedLong> undefined = memory.getUninitialized(addr, addr + len);
|
||||
RangeSet<UnsignedLong> undefined = memory.getUninitialized(addr, addr + len - 1);
|
||||
// Do the reads in parallel
|
||||
AsyncFence fence = new AsyncFence();
|
||||
for (Range<UnsignedLong> rng : undefined.asRanges()) {
|
||||
@@ -115,7 +133,8 @@ public class CachedMemory implements MemoryReader, MemoryWriter {
|
||||
}
|
||||
}
|
||||
NavigableMap<UnsignedLong, PendingRead> applicablePending =
|
||||
pendingByLoc.subMap(rng.lowerEndpoint(), true, rng.upperEndpoint(), false);
|
||||
pendingByLoc.subMap(rng.lowerEndpoint(), true, rng.upperEndpoint(),
|
||||
rng.upperBoundType() == BoundType.CLOSED);
|
||||
for (Map.Entry<UnsignedLong, PendingRead> ent : applicablePending.entrySet()) {
|
||||
PendingRead pending = ent.getValue();
|
||||
if (pending.future.isCompletedExceptionally()) {
|
||||
@@ -128,7 +147,10 @@ public class CachedMemory implements MemoryReader, MemoryWriter {
|
||||
// Now we're left with a set of needed ranges. Make a request for each
|
||||
for (Range<UnsignedLong> needed : needRequests.asRanges()) {
|
||||
final UnsignedLong lower = needed.lowerEndpoint();
|
||||
final UnsignedLong upper = needed.upperEndpoint();
|
||||
// NB. upper is only used in size computation, so overflow to 0 is no big deal
|
||||
final UnsignedLong upper = needed.upperBoundType() == BoundType.CLOSED
|
||||
? needed.upperEndpoint().plus(UnsignedLong.ONE)
|
||||
: needed.upperEndpoint();
|
||||
/*Msg.debug(this,
|
||||
"Need to read: [" + lower.toString(16) + ":" + upper.toString(16) + ")");*/
|
||||
CompletableFuture<byte[]> futureRead =
|
||||
|
||||
+149
-147
@@ -17,57 +17,82 @@ package ghidra.dbg.memory;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Deque;
|
||||
import java.util.LinkedList;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import mockit.Expectations;
|
||||
import mockit.Mocked;
|
||||
|
||||
public class CachedMemoryTest {
|
||||
|
||||
class ReadRecord extends CompletableFuture<byte[]> {
|
||||
static class RequestRecord<T> {
|
||||
final CompletableFuture<T> future = new CompletableFuture<>();
|
||||
final long address;
|
||||
|
||||
public RequestRecord(long address) {
|
||||
this.address = address;
|
||||
}
|
||||
}
|
||||
|
||||
static class ReadRequestRecord extends RequestRecord<byte[]> {
|
||||
final int length;
|
||||
|
||||
public ReadRecord(long address, int length) {
|
||||
this.address = address;
|
||||
public ReadRequestRecord(long address, int length) {
|
||||
super(address);
|
||||
this.length = length;
|
||||
}
|
||||
}
|
||||
|
||||
class WriteRecord extends CompletableFuture<Void> {
|
||||
final long address;
|
||||
static class WriteRequestRecord extends RequestRecord<Void> {
|
||||
final byte[] data;
|
||||
|
||||
public WriteRecord(long address, byte[] data) {
|
||||
this.address = address;
|
||||
public WriteRequestRecord(long address, byte[] data) {
|
||||
super(address);
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
|
||||
class DummyMemory implements MemoryReader, MemoryWriter {
|
||||
final List<CompletableFuture<?>> record = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> writeMemory(long address, byte[] data) {
|
||||
return null;
|
||||
}
|
||||
static class TestMemoryReaderWriter implements MemoryReader, MemoryWriter {
|
||||
Deque<RequestRecord<?>> earlies = new LinkedList<>();
|
||||
Deque<RequestRecord<?>> requests = new LinkedList<>();
|
||||
|
||||
@Override
|
||||
public CompletableFuture<byte[]> readMemory(long address, int length) {
|
||||
return new ReadRecord(address, length);
|
||||
RequestRecord<?> early = earlies.poll();
|
||||
if (early != null) {
|
||||
ReadRequestRecord req = (ReadRequestRecord) early;
|
||||
assertEquals(req.address, address);
|
||||
assertEquals(req.length, length);
|
||||
return req.future;
|
||||
}
|
||||
ReadRequestRecord req = new ReadRequestRecord(address, length);
|
||||
requests.add(req);
|
||||
return req.future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> writeMemory(long address, byte[] data) {
|
||||
WriteRequestRecord req = new WriteRequestRecord(address, data);
|
||||
requests.add(req);
|
||||
return req.future;
|
||||
}
|
||||
|
||||
public void expectEarlyRead(long address, byte[] data) {
|
||||
ReadRequestRecord req = new ReadRequestRecord(address, data.length);
|
||||
req.future.complete(data);
|
||||
earlies.add(req);
|
||||
}
|
||||
|
||||
public ReadRequestRecord assertPollRead() {
|
||||
return (ReadRequestRecord) requests.remove();
|
||||
}
|
||||
|
||||
public WriteRequestRecord assertPollWrite() {
|
||||
return (WriteRequestRecord) requests.remove();
|
||||
}
|
||||
}
|
||||
|
||||
interface MemoryReaderWriter extends MemoryReader, MemoryWriter {
|
||||
// Nothing new, just combined interfaces
|
||||
}
|
||||
|
||||
@Mocked
|
||||
protected MemoryReaderWriter memory;
|
||||
protected TestMemoryReaderWriter memory = new TestMemoryReaderWriter();
|
||||
|
||||
byte[] inc(int len) {
|
||||
byte[] result = new byte[len];
|
||||
@@ -83,30 +108,37 @@ public class CachedMemoryTest {
|
||||
|
||||
@Test
|
||||
public void testSingleRead() throws Exception {
|
||||
final CompletableFuture<byte[]> raw = new CompletableFuture<>();
|
||||
new Expectations() {
|
||||
{
|
||||
memory.readMemory(1234, 90);
|
||||
result = raw;
|
||||
}
|
||||
};
|
||||
|
||||
CachedMemory cache = new CachedMemory(memory, memory);
|
||||
CompletableFuture<byte[]> future = cache.readMemory(1234, 90);
|
||||
raw.complete(inc(90));
|
||||
|
||||
ReadRequestRecord rec = memory.assertPollRead();
|
||||
assertEquals(1234, rec.address);
|
||||
assertEquals(90, rec.length);
|
||||
|
||||
rec.future.complete(inc(90));
|
||||
byte[] arr = future.get(1000, TimeUnit.MILLISECONDS);
|
||||
|
||||
assertArrayEquals(inc(90), arr);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleReadIncludesMax() throws Exception {
|
||||
CachedMemory cache = new CachedMemory(memory, memory);
|
||||
CompletableFuture<byte[]> future = cache.readMemory(-4, 4);
|
||||
|
||||
ReadRequestRecord rec = memory.assertPollRead();
|
||||
assertEquals(-4, rec.address);
|
||||
assertEquals(4, rec.length);
|
||||
|
||||
rec.future.complete(new byte[] { 1, 2, 3, 4 });
|
||||
byte[] arr = future.get(1000, TimeUnit.MILLISECONDS);
|
||||
|
||||
assertArrayEquals(new byte[] { 1, 2, 3, 4 }, arr);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleReadCompletedEarly() throws Exception {
|
||||
new Expectations() {
|
||||
{
|
||||
memory.readMemory(1234, 90);
|
||||
result = CompletableFuture.completedFuture(inc(90));
|
||||
}
|
||||
};
|
||||
memory.expectEarlyRead(1234, inc(90));
|
||||
|
||||
CachedMemory cache = new CachedMemory(memory, memory);
|
||||
CompletableFuture<byte[]> future = cache.readMemory(1234, 90);
|
||||
@@ -117,25 +149,20 @@ public class CachedMemoryTest {
|
||||
|
||||
@Test
|
||||
public void testOverlappingSequentialReads() throws Exception {
|
||||
final CompletableFuture<byte[]> firstRaw = new CompletableFuture<>();
|
||||
final CompletableFuture<byte[]> secondRaw = new CompletableFuture<>();
|
||||
new Expectations() {
|
||||
{
|
||||
memory.readMemory(1234, 100);
|
||||
result = firstRaw;
|
||||
memory.readMemory(1334, 50);
|
||||
result = secondRaw;
|
||||
}
|
||||
};
|
||||
|
||||
CachedMemory cache = new CachedMemory(memory, memory);
|
||||
|
||||
CompletableFuture<byte[]> first = cache.readMemory(1234, 100);
|
||||
firstRaw.complete(inc(100));
|
||||
ReadRequestRecord req1 = memory.assertPollRead();
|
||||
assertEquals(1234, req1.address);
|
||||
assertEquals(100, req1.length);
|
||||
req1.future.complete(inc(100));
|
||||
byte[] firstArr = first.get(1000, TimeUnit.MILLISECONDS);
|
||||
|
||||
CompletableFuture<byte[]> second = cache.readMemory(1284, 100);
|
||||
secondRaw.complete(inc(50));
|
||||
ReadRequestRecord req2 = memory.assertPollRead();
|
||||
assertEquals(1334, req2.address);
|
||||
assertEquals(50, req2.length);
|
||||
req2.future.complete(inc(50));
|
||||
byte[] secondArr = second.get(1000, TimeUnit.MILLISECONDS);
|
||||
|
||||
assertArrayEquals(inc(100), firstArr);
|
||||
@@ -148,23 +175,20 @@ public class CachedMemoryTest {
|
||||
|
||||
@Test
|
||||
public void testOverlappingParallelReads() throws Exception {
|
||||
final CompletableFuture<byte[]> firstRaw = new CompletableFuture<>();
|
||||
final CompletableFuture<byte[]> secondRaw = new CompletableFuture<>();
|
||||
new Expectations() {
|
||||
{
|
||||
memory.readMemory(1234, 100);
|
||||
result = firstRaw;
|
||||
memory.readMemory(1334, 50);
|
||||
result = secondRaw;
|
||||
}
|
||||
};
|
||||
|
||||
CachedMemory cache = new CachedMemory(memory, memory);
|
||||
|
||||
CompletableFuture<byte[]> first = cache.readMemory(1234, 100);
|
||||
ReadRequestRecord req1 = memory.assertPollRead();
|
||||
assertEquals(1234, req1.address);
|
||||
assertEquals(100, req1.length);
|
||||
|
||||
CompletableFuture<byte[]> second = cache.readMemory(1284, 100);
|
||||
firstRaw.complete(inc(100));
|
||||
secondRaw.complete(inc(50));
|
||||
ReadRequestRecord req2 = memory.assertPollRead();
|
||||
assertEquals(1334, req2.address);
|
||||
assertEquals(50, req2.length);
|
||||
|
||||
req1.future.complete(inc(100));
|
||||
req2.future.complete(inc(50));
|
||||
byte[] firstArr = first.get(1000, TimeUnit.MILLISECONDS);
|
||||
byte[] secondArr = second.get(1000, TimeUnit.MILLISECONDS);
|
||||
|
||||
@@ -178,28 +202,24 @@ public class CachedMemoryTest {
|
||||
|
||||
@Test
|
||||
public void testSameStartsGrowingParallelReads() throws Exception {
|
||||
final CompletableFuture<byte[]> firstRaw = new CompletableFuture<>();
|
||||
final CompletableFuture<byte[]> secondRaw = new CompletableFuture<>();
|
||||
new Expectations() {
|
||||
{
|
||||
memory.readMemory(1234, 50);
|
||||
result = firstRaw;
|
||||
memory.readMemory(1284, 50);
|
||||
result = secondRaw;
|
||||
}
|
||||
};
|
||||
|
||||
CachedMemory cache = new CachedMemory(memory, memory);
|
||||
|
||||
CompletableFuture<byte[]> first = cache.readMemory(1234, 50);
|
||||
ReadRequestRecord req1 = memory.assertPollRead();
|
||||
assertEquals(1234, req1.address);
|
||||
assertEquals(50, req1.length);
|
||||
|
||||
CompletableFuture<byte[]> second = cache.readMemory(1234, 100);
|
||||
ReadRequestRecord req2 = memory.assertPollRead();
|
||||
assertEquals(1284, req2.address);
|
||||
assertEquals(50, req2.length);
|
||||
|
||||
assertFalse(first.isDone());
|
||||
firstRaw.complete(inc(50));
|
||||
req1.future.complete(inc(50));
|
||||
byte[] firstArr = first.get(1000, TimeUnit.MILLISECONDS);
|
||||
|
||||
assertFalse(second.isDone());
|
||||
secondRaw.complete(inc(50));
|
||||
req2.future.complete(inc(50));
|
||||
byte[] secondArr = second.get(1000, TimeUnit.MILLISECONDS);
|
||||
|
||||
assertArrayEquals(inc(50), firstArr);
|
||||
@@ -212,23 +232,20 @@ public class CachedMemoryTest {
|
||||
|
||||
@Test
|
||||
public void testLargeOffsetsParallelReads() throws Exception {
|
||||
final CompletableFuture<byte[]> firstRaw = new CompletableFuture<>();
|
||||
final CompletableFuture<byte[]> secondRaw = new CompletableFuture<>();
|
||||
new Expectations() {
|
||||
{
|
||||
memory.readMemory(0x8000000000000000L, 100);
|
||||
result = firstRaw;
|
||||
memory.readMemory(0x8000000000000000L + 100, 50);
|
||||
result = secondRaw;
|
||||
}
|
||||
};
|
||||
|
||||
CachedMemory cache = new CachedMemory(memory, memory);
|
||||
|
||||
CompletableFuture<byte[]> first = cache.readMemory(0x8000000000000000L, 100);
|
||||
CompletableFuture<byte[]> second = cache.readMemory(0x8000000000000000L + 50, 100);
|
||||
firstRaw.complete(inc(100));
|
||||
secondRaw.complete(inc(50));
|
||||
CompletableFuture<byte[]> first = cache.readMemory(0x8000_0000_0000_0000L, 100);
|
||||
ReadRequestRecord req1 = memory.assertPollRead();
|
||||
assertEquals(0x8000_0000_0000_0000L, req1.address);
|
||||
assertEquals(100, req1.length);
|
||||
|
||||
CompletableFuture<byte[]> second = cache.readMemory(0x8000_0000_0000_0000L + 50, 100);
|
||||
ReadRequestRecord req2 = memory.assertPollRead();
|
||||
assertEquals(0x8000_0000_0000_0000L + 100, req2.address);
|
||||
assertEquals(50, req2.length);
|
||||
|
||||
req1.future.complete(inc(100));
|
||||
req2.future.complete(inc(50));
|
||||
byte[] firstArr = first.get(1000, TimeUnit.MILLISECONDS);
|
||||
byte[] secondArr = second.get(1000, TimeUnit.MILLISECONDS);
|
||||
|
||||
@@ -242,22 +259,15 @@ public class CachedMemoryTest {
|
||||
|
||||
@Test
|
||||
public void testErroneousRead() throws Exception {
|
||||
final CompletableFuture<byte[]> firstErr = new CompletableFuture<>();
|
||||
final CompletableFuture<byte[]> secondRaw = new CompletableFuture<>();
|
||||
new Expectations() {
|
||||
{
|
||||
memory.readMemory(0, 100);
|
||||
result = firstErr;
|
||||
memory.readMemory(50, 100);
|
||||
result = secondRaw;
|
||||
}
|
||||
};
|
||||
|
||||
CachedMemory cache = new CachedMemory(memory, memory);
|
||||
|
||||
CompletableFuture<byte[]> first = cache.readMemory(0, 100);
|
||||
ReadRequestRecord req1 = memory.assertPollRead();
|
||||
assertEquals(0, req1.address);
|
||||
assertEquals(100, req1.length);
|
||||
|
||||
Throwable sentinel = new AssertionError("Sentinel");
|
||||
firstErr.completeExceptionally(sentinel);
|
||||
req1.future.completeExceptionally(sentinel);
|
||||
try {
|
||||
first.get(1000, TimeUnit.MILLISECONDS);
|
||||
fail();
|
||||
@@ -267,7 +277,10 @@ public class CachedMemoryTest {
|
||||
}
|
||||
|
||||
CompletableFuture<byte[]> second = cache.readMemory(50, 100);
|
||||
secondRaw.complete(inc(100));
|
||||
ReadRequestRecord req2 = memory.assertPollRead();
|
||||
assertEquals(50, req2.address);
|
||||
assertEquals(100, req2.length);
|
||||
req2.future.complete(inc(100));
|
||||
byte[] secondArr = second.get(1000, TimeUnit.MILLISECONDS);
|
||||
|
||||
assertArrayEquals(inc(100), secondArr);
|
||||
@@ -275,26 +288,23 @@ public class CachedMemoryTest {
|
||||
|
||||
@Test
|
||||
public void testPartialResult() throws Exception {
|
||||
final CompletableFuture<byte[]> firstPartial = new CompletableFuture<>();
|
||||
final CompletableFuture<byte[]> secondRaw = new CompletableFuture<>();
|
||||
new Expectations() {
|
||||
{
|
||||
memory.readMemory(0, 100);
|
||||
result = firstPartial;
|
||||
memory.readMemory(50, 50);
|
||||
result = secondRaw;
|
||||
}
|
||||
};
|
||||
|
||||
CachedMemory cache = new CachedMemory(memory, memory);
|
||||
|
||||
CompletableFuture<byte[]> first = cache.readMemory(0, 100);
|
||||
firstPartial.complete(inc(50)); // request was for 100!
|
||||
ReadRequestRecord req1 = memory.assertPollRead();
|
||||
assertEquals(0, req1.address);
|
||||
assertEquals(100, req1.length);
|
||||
|
||||
req1.future.complete(inc(50)); // request was for 100!
|
||||
byte[] firstArr = first.get(1000, TimeUnit.MILLISECONDS);
|
||||
assertArrayEquals(inc(50), firstArr);
|
||||
|
||||
CompletableFuture<byte[]> second = cache.readMemory(25, 75);
|
||||
secondRaw.complete(inc(50));
|
||||
ReadRequestRecord req2 = memory.assertPollRead();
|
||||
assertEquals(50, req2.address);
|
||||
assertEquals(50, req2.length);
|
||||
|
||||
req2.future.complete(inc(50));
|
||||
byte[] secondArr = second.get(1000, TimeUnit.MILLISECONDS);
|
||||
|
||||
byte[] dinc = new byte[75];
|
||||
@@ -305,24 +315,20 @@ public class CachedMemoryTest {
|
||||
|
||||
@Test
|
||||
public void testDisjointParallellFirstErrs() throws Exception {
|
||||
final CompletableFuture<byte[]> firstErr = new CompletableFuture<>();
|
||||
final CompletableFuture<byte[]> secondRaw = new CompletableFuture<>();
|
||||
new Expectations() {
|
||||
{
|
||||
memory.readMemory(0, 25);
|
||||
result = firstErr;
|
||||
memory.readMemory(50, 25);
|
||||
result = secondRaw;
|
||||
}
|
||||
};
|
||||
|
||||
CachedMemory cache = new CachedMemory(memory, memory);
|
||||
|
||||
CompletableFuture<byte[]> first = cache.readMemory(0, 25);
|
||||
ReadRequestRecord req1 = memory.assertPollRead();
|
||||
assertEquals(0, req1.address);
|
||||
assertEquals(25, req1.length);
|
||||
|
||||
CompletableFuture<byte[]> second = cache.readMemory(50, 25);
|
||||
ReadRequestRecord req2 = memory.assertPollRead();
|
||||
assertEquals(50, req2.address);
|
||||
assertEquals(25, req2.length);
|
||||
|
||||
Throwable sentinel = new AssertionError("Sentinel");
|
||||
firstErr.completeExceptionally(sentinel);
|
||||
req1.future.completeExceptionally(sentinel);
|
||||
try {
|
||||
first.get(0, TimeUnit.MILLISECONDS);
|
||||
fail();
|
||||
@@ -331,31 +337,27 @@ public class CachedMemoryTest {
|
||||
assertEquals(sentinel, e.getCause());
|
||||
}
|
||||
|
||||
secondRaw.complete(inc(25));
|
||||
req2.future.complete(inc(25));
|
||||
byte[] secondArr = second.get(1000, TimeUnit.MILLISECONDS);
|
||||
assertArrayEquals(inc(25), secondArr);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPartialFromErr() throws Exception {
|
||||
final CompletableFuture<byte[]> firstErr = new CompletableFuture<>();
|
||||
final CompletableFuture<byte[]> secondRaw = new CompletableFuture<>();
|
||||
new Expectations() {
|
||||
{
|
||||
memory.readMemory(50, 50);
|
||||
result = firstErr;
|
||||
memory.readMemory(0, 50);
|
||||
result = secondRaw;
|
||||
}
|
||||
};
|
||||
|
||||
CachedMemory cache = new CachedMemory(memory, memory);
|
||||
|
||||
CompletableFuture<byte[]> first = cache.readMemory(50, 50);
|
||||
ReadRequestRecord req1 = memory.assertPollRead();
|
||||
assertEquals(50, req1.address);
|
||||
assertEquals(50, req1.length);
|
||||
|
||||
CompletableFuture<byte[]> second = cache.readMemory(0, 100);
|
||||
ReadRequestRecord req2 = memory.assertPollRead();
|
||||
assertEquals(0, req2.address);
|
||||
assertEquals(50, req2.length);
|
||||
|
||||
Throwable sentinel = new AssertionError("Sentinel");
|
||||
firstErr.completeExceptionally(sentinel);
|
||||
req1.future.completeExceptionally(sentinel);
|
||||
try {
|
||||
first.get(0, TimeUnit.MILLISECONDS);
|
||||
fail();
|
||||
@@ -364,7 +366,7 @@ public class CachedMemoryTest {
|
||||
assertEquals(sentinel, e.getCause());
|
||||
}
|
||||
// First should still succeed partially
|
||||
secondRaw.complete(inc(50));
|
||||
req2.future.complete(inc(50));
|
||||
byte[] secondArr = second.get(1000, TimeUnit.MILLISECONDS);
|
||||
assertArrayEquals(inc(50), secondArr);
|
||||
}
|
||||
|
||||
+2
-1
@@ -48,7 +48,8 @@ public abstract class AbstractCheckedTraceCachedWriteBytesPcodeExecutorState
|
||||
|
||||
@Override
|
||||
public byte[] read(long offset, int size) {
|
||||
RangeSet<UnsignedLong> uninitialized = cache.getUninitialized(offset, offset + size);
|
||||
RangeSet<UnsignedLong> uninitialized =
|
||||
cache.getUninitialized(offset, offset + size - 1);
|
||||
|
||||
if (!uninitialized.isEmpty()) {
|
||||
size = checkUninitialized(source, space.getAddress(offset), size,
|
||||
|
||||
+1
-1
@@ -155,7 +155,7 @@ public class TraceCachedWriteBytesPcodeExecutorState
|
||||
// TODO: Warn or bail when reading UNKNOWN bytes
|
||||
// NOTE: Read without regard to gaps
|
||||
// NOTE: Cannot write those gaps, though!!!
|
||||
readUninitializedFromSource(cache.getUninitialized(offset, offset + size));
|
||||
readUninitializedFromSource(cache.getUninitialized(offset, offset + size - 1));
|
||||
}
|
||||
return readCached(offset, size);
|
||||
}
|
||||
|
||||
+20
-12
@@ -716,30 +716,38 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
|
||||
}
|
||||
}
|
||||
|
||||
public void updateViewsAddBlock(DBTraceMemoryRegion region) {
|
||||
allViews(v -> v.updateMemoryAddBlock(region));
|
||||
public void updateViewsAddRegionBlock(DBTraceMemoryRegion region) {
|
||||
allViews(v -> v.updateMemoryAddRegionBlock(region));
|
||||
}
|
||||
|
||||
public void updateViewsChangeBlockName(DBTraceMemoryRegion region) {
|
||||
allViews(v -> v.updateMemoryChangeBlockName(region));
|
||||
public void updateViewsChangeRegionBlockName(DBTraceMemoryRegion region) {
|
||||
allViews(v -> v.updateMemoryChangeRegionBlockName(region));
|
||||
}
|
||||
|
||||
public void updateViewsChangeBlockFlags(DBTraceMemoryRegion region) {
|
||||
allViews(v -> v.updateMemoryChangeBlockFlags(region));
|
||||
public void updateViewsChangeRegionBlockFlags(DBTraceMemoryRegion region) {
|
||||
allViews(v -> v.updateMemoryChangeRegionBlockFlags(region));
|
||||
}
|
||||
|
||||
public void updateViewsChangeBlockRange(DBTraceMemoryRegion region,
|
||||
public void updateViewsChangeRegionBlockRange(DBTraceMemoryRegion region,
|
||||
AddressRange oldRange, AddressRange newRange) {
|
||||
allViews(v -> v.updateMemoryChangeBlockRange(region, oldRange, newRange));
|
||||
allViews(v -> v.updateMemoryChangeRegionBlockRange(region, oldRange, newRange));
|
||||
}
|
||||
|
||||
public void updateViewsChangeBlockLifespan(DBTraceMemoryRegion region,
|
||||
public void updateViewsChangeRegionBlockLifespan(DBTraceMemoryRegion region,
|
||||
Range<Long> oldLifespan, Range<Long> newLifespan) {
|
||||
allViews(v -> v.updateMemoryChangeBlockLifespan(region, oldLifespan, newLifespan));
|
||||
allViews(v -> v.updateMemoryChangeRegionBlockLifespan(region, oldLifespan, newLifespan));
|
||||
}
|
||||
|
||||
public void updateViewsDeleteBlock(DBTraceMemoryRegion region) {
|
||||
allViews(v -> v.updateMemoryDeleteBlock(region));
|
||||
public void updateViewsDeleteRegionBlock(DBTraceMemoryRegion region) {
|
||||
allViews(v -> v.updateMemoryDeleteRegionBlock(region));
|
||||
}
|
||||
|
||||
public void updateViewsAddSpaceBlock(AddressSpace space) {
|
||||
allViews(v -> v.updateMemoryAddSpaceBlock(space));
|
||||
}
|
||||
|
||||
public void updateViewsDeleteSpaceBlock(AddressSpace space) {
|
||||
allViews(v -> v.updateMemoryDeleteSpaceBlock(space));
|
||||
}
|
||||
|
||||
public void updateViewsRefreshBlocks() {
|
||||
|
||||
+4
@@ -255,6 +255,7 @@ public class DBTraceOverlaySpaceAdapter implements DBTraceManager {
|
||||
// Only if it succeeds do we store the record
|
||||
DBTraceOverlaySpaceEntry ent = overlayStore.create();
|
||||
ent.set(space.getName(), base.getName());
|
||||
trace.updateViewsAddSpaceBlock(space);
|
||||
return space;
|
||||
}
|
||||
}
|
||||
@@ -268,7 +269,10 @@ public class DBTraceOverlaySpaceAdapter implements DBTraceManager {
|
||||
}
|
||||
overlayStore.delete(exists);
|
||||
TraceAddressFactory factory = trace.getInternalAddressFactory();
|
||||
AddressSpace space = factory.getAddressSpace(name);
|
||||
assert space != null;
|
||||
factory.removeOverlaySpace(name);
|
||||
trace.updateViewsDeleteSpaceBlock(space);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-6
@@ -154,7 +154,7 @@ public class DBTraceMemoryRegion
|
||||
try (LockHold hold = LockHold.lock(space.lock.writeLock())) {
|
||||
this.name = name;
|
||||
update(NAME_COLUMN);
|
||||
space.trace.updateViewsChangeBlockName(this);
|
||||
space.trace.updateViewsChangeRegionBlockName(this);
|
||||
}
|
||||
space.trace.setChanged(
|
||||
new TraceChangeRecord<>(TraceMemoryRegionChangeType.CHANGED, space, this));
|
||||
@@ -175,7 +175,7 @@ public class DBTraceMemoryRegion
|
||||
checkPathConflicts(newLifespan, path);
|
||||
Range<Long> oldLifespan = getLifespan();
|
||||
doSetLifespan(newLifespan);
|
||||
space.trace.updateViewsChangeBlockLifespan(this, oldLifespan, newLifespan);
|
||||
space.trace.updateViewsChangeRegionBlockLifespan(this, oldLifespan, newLifespan);
|
||||
space.trace.setChanged(
|
||||
new TraceChangeRecord<>(TraceMemoryRegionChangeType.LIFESPAN_CHANGED,
|
||||
space, this, oldLifespan, newLifespan));
|
||||
@@ -218,7 +218,7 @@ public class DBTraceMemoryRegion
|
||||
oldRange = range;
|
||||
checkOverlapConflicts(lifespan, newRange);
|
||||
doSetRange(newRange);
|
||||
space.trace.updateViewsChangeBlockRange(this, oldRange, newRange);
|
||||
space.trace.updateViewsChangeRegionBlockRange(this, oldRange, newRange);
|
||||
}
|
||||
space.trace.setChanged(
|
||||
new TraceChangeRecord<>(TraceMemoryRegionChangeType.CHANGED, space, this));
|
||||
@@ -278,7 +278,7 @@ public class DBTraceMemoryRegion
|
||||
this.flags.add(f);
|
||||
}
|
||||
update(FLAGS_COLUMN);
|
||||
space.trace.updateViewsChangeBlockFlags(this);
|
||||
space.trace.updateViewsChangeRegionBlockFlags(this);
|
||||
}
|
||||
space.trace.setChanged(
|
||||
new TraceChangeRecord<>(TraceMemoryRegionChangeType.CHANGED, space, this));
|
||||
@@ -293,7 +293,7 @@ public class DBTraceMemoryRegion
|
||||
this.flags.add(f);
|
||||
}
|
||||
update(FLAGS_COLUMN);
|
||||
space.trace.updateViewsChangeBlockFlags(this);
|
||||
space.trace.updateViewsChangeRegionBlockFlags(this);
|
||||
}
|
||||
space.trace.setChanged(
|
||||
new TraceChangeRecord<>(TraceMemoryRegionChangeType.CHANGED, space, this));
|
||||
@@ -308,7 +308,7 @@ public class DBTraceMemoryRegion
|
||||
this.flags.remove(f);
|
||||
}
|
||||
update(FLAGS_COLUMN);
|
||||
space.trace.updateViewsChangeBlockFlags(this);
|
||||
space.trace.updateViewsChangeRegionBlockFlags(this);
|
||||
}
|
||||
space.trace.setChanged(
|
||||
new TraceChangeRecord<>(TraceMemoryRegionChangeType.CHANGED, space, this));
|
||||
|
||||
+2
-2
@@ -161,7 +161,7 @@ public class DBTraceMemorySpace implements Unfinished, TraceMemorySpace, DBTrace
|
||||
DBTraceMemoryRegion region =
|
||||
regionMapSpace.put(new ImmutableTraceAddressSnapRange(range, lifespan), null);
|
||||
region.set(path, path, flags);
|
||||
trace.updateViewsAddBlock(region);
|
||||
trace.updateViewsAddRegionBlock(region);
|
||||
trace.setChanged(
|
||||
new TraceChangeRecord<>(TraceMemoryRegionChangeType.ADDED, this, region));
|
||||
return region;
|
||||
@@ -244,7 +244,7 @@ public class DBTraceMemorySpace implements Unfinished, TraceMemorySpace, DBTrace
|
||||
try (LockHold hold = LockHold.lock(lock.writeLock())) {
|
||||
regionMapSpace.deleteData(region);
|
||||
regionCache.remove(region);
|
||||
trace.updateViewsDeleteBlock(region);
|
||||
trace.updateViewsDeleteRegionBlock(region);
|
||||
trace.setChanged(
|
||||
new TraceChangeRecord<>(TraceMemoryRegionChangeType.DELETED, this, region));
|
||||
}
|
||||
|
||||
+1
@@ -69,6 +69,7 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
|
||||
DBTraceMemorySpace mem = trace.getMemoryManager().get(this, false);
|
||||
if (mem == null) {
|
||||
// TODO: 0-fill instead? Will need to check memory space bounds.
|
||||
return 0;
|
||||
}
|
||||
return mem.getViewBytes(program.snap, address.add(addressOffset), buffer);
|
||||
}
|
||||
|
||||
+58
-9
@@ -19,6 +19,7 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import com.google.common.cache.RemovalNotification;
|
||||
|
||||
@@ -42,6 +43,7 @@ public abstract class AbstractDBTraceProgramViewMemory
|
||||
protected final DBTraceMemoryManager memoryManager;
|
||||
|
||||
protected AddressSetView addressSet;
|
||||
protected boolean forceFullView = false;
|
||||
protected long snap;
|
||||
|
||||
public AbstractDBTraceProgramViewMemory(DBTraceProgramView program) {
|
||||
@@ -50,16 +52,57 @@ public abstract class AbstractDBTraceProgramViewMemory
|
||||
setSnap(program.snap);
|
||||
}
|
||||
|
||||
protected void blockRemoved(
|
||||
RemovalNotification<DBTraceMemoryRegion, DBTraceProgramViewMemoryBlock> rn) {
|
||||
protected void regionBlockRemoved(
|
||||
RemovalNotification<DBTraceMemoryRegion, DBTraceProgramViewMemoryRegionBlock> rn) {
|
||||
// Nothing
|
||||
}
|
||||
|
||||
protected void spaceBlockRemoved(
|
||||
RemovalNotification<AddressSpace, DBTraceProgramViewMemorySpaceBlock> rn) {
|
||||
// Nothing
|
||||
}
|
||||
|
||||
protected abstract void recomputeAddressSet();
|
||||
|
||||
protected void forPhysicalSpaces(Consumer<AddressSpace> consumer) {
|
||||
for (AddressSpace space : program.getAddressFactory().getAddressSpaces()) {
|
||||
// NB. Overlay's isMemory depends on its base space
|
||||
// TODO: Allow other?
|
||||
// For some reason "other" is omitted from factory.getAddressSet
|
||||
if (space.isMemorySpace() && space.getType() != AddressSpace.TYPE_OTHER) {
|
||||
consumer.accept(space);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void computeFullAdddressSet() {
|
||||
AddressSet temp = new AddressSet();
|
||||
forPhysicalSpaces(space -> temp.add(space.getMinAddress(), space.getMaxAddress()));
|
||||
addressSet = temp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setForceFullView(boolean forceFullView) {
|
||||
this.forceFullView = forceFullView;
|
||||
if (forceFullView) {
|
||||
computeFullAdddressSet();
|
||||
}
|
||||
else {
|
||||
recomputeAddressSet();
|
||||
}
|
||||
program.fireObjectRestored();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isForceFullView() {
|
||||
return forceFullView;
|
||||
}
|
||||
|
||||
void setSnap(long snap) {
|
||||
this.snap = snap;
|
||||
recomputeAddressSet();
|
||||
if (!forceFullView) {
|
||||
recomputeAddressSet();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -463,17 +506,23 @@ public abstract class AbstractDBTraceProgramViewMemory
|
||||
}
|
||||
|
||||
protected synchronized void addRange(AddressRange range) {
|
||||
addressSet = addressSet.union(new AddressSet(range));
|
||||
if (!forceFullView) {
|
||||
addressSet = addressSet.union(new AddressSet(range));
|
||||
}
|
||||
}
|
||||
|
||||
protected synchronized void removeRange(AddressRange range) {
|
||||
addressSet = addressSet.subtract(new AddressSet(range));
|
||||
if (!forceFullView) {
|
||||
addressSet = addressSet.subtract(new AddressSet(range));
|
||||
}
|
||||
}
|
||||
|
||||
protected synchronized void changeRange(AddressRange remove, AddressRange add) {
|
||||
AddressSet temp = new AddressSet(addressSet);
|
||||
temp.delete(remove);
|
||||
temp.add(add);
|
||||
addressSet = temp;
|
||||
if (!forceFullView) {
|
||||
AddressSet temp = new AddressSet(addressSet);
|
||||
temp.delete(remove);
|
||||
temp.add(add);
|
||||
addressSet = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+47
-110
@@ -16,42 +16,39 @@
|
||||
package ghidra.trace.database.program;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.framework.store.LockException;
|
||||
import ghidra.program.database.mem.ByteMappingScheme;
|
||||
import ghidra.program.database.mem.FileBytes;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.mem.*;
|
||||
import ghidra.trace.database.memory.DBTraceMemoryRegion;
|
||||
import ghidra.trace.database.memory.DBTraceMemorySpace;
|
||||
import ghidra.trace.model.memory.TraceMemoryFlag;
|
||||
import ghidra.trace.model.memory.TraceMemorySpaceInputStream;
|
||||
import ghidra.util.MathUtilities;
|
||||
|
||||
// TODO: Proper locking all over here
|
||||
public class DBTraceProgramViewMemoryBlock implements MemoryBlock {
|
||||
public abstract class AbstractDBTraceProgramViewMemoryBlock implements MemoryBlock {
|
||||
|
||||
private class DBTraceProgramViewMemoryBlockSourceInfo implements MemoryBlockSourceInfo {
|
||||
private class MyMemoryBlockSourceInfo implements MemoryBlockSourceInfo {
|
||||
@Override
|
||||
public long getLength() {
|
||||
return region.getLength();
|
||||
return getMemoryBlock().getSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address getMinAddress() {
|
||||
return region.getMinAddress();
|
||||
return getMemoryBlock().getStart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address getMaxAddress() {
|
||||
return region.getMaxAddress();
|
||||
return getMemoryBlock().getEnd();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Trace region: " + region;
|
||||
return getInfoDescription();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -81,12 +78,12 @@ public class DBTraceProgramViewMemoryBlock implements MemoryBlock {
|
||||
|
||||
@Override
|
||||
public MemoryBlock getMemoryBlock() {
|
||||
return DBTraceProgramViewMemoryBlock.this;
|
||||
return AbstractDBTraceProgramViewMemoryBlock.this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Address address) {
|
||||
return region.getRange().contains(address);
|
||||
return getMemoryBlock().contains(address);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -95,15 +92,26 @@ public class DBTraceProgramViewMemoryBlock implements MemoryBlock {
|
||||
}
|
||||
}
|
||||
|
||||
private final DBTraceProgramView program;
|
||||
private final DBTraceMemoryRegion region;
|
||||
|
||||
protected final DBTraceProgramView program;
|
||||
private final List<MemoryBlockSourceInfo> info =
|
||||
Collections.singletonList(new DBTraceProgramViewMemoryBlockSourceInfo());
|
||||
Collections.singletonList(new MyMemoryBlockSourceInfo());
|
||||
|
||||
public DBTraceProgramViewMemoryBlock(DBTraceProgramView program, DBTraceMemoryRegion region) {
|
||||
protected AbstractDBTraceProgramViewMemoryBlock(DBTraceProgramView program) {
|
||||
this.program = program;
|
||||
this.region = region;
|
||||
}
|
||||
|
||||
protected abstract String getInfoDescription();
|
||||
|
||||
protected AddressSpace getAddressSpace() {
|
||||
return getStart().getAddressSpace();
|
||||
}
|
||||
|
||||
protected DBTraceMemorySpace getMemorySpace() {
|
||||
return program.trace.getMemoryManager().getMemorySpace(getAddressSpace(), false);
|
||||
}
|
||||
|
||||
protected AddressRange getAddressRange() {
|
||||
return new AddressRangeImpl(getStart(), getEnd());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -111,112 +119,40 @@ public class DBTraceProgramViewMemoryBlock implements MemoryBlock {
|
||||
return this.getStart().compareTo(that.getStart());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPermissions(boolean read, boolean write, boolean execute) {
|
||||
region.setRead(read);
|
||||
region.setWrite(write);
|
||||
region.setExecute(execute);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPermissions() {
|
||||
int bits = 0;
|
||||
for (TraceMemoryFlag flag : region.getFlags()) {
|
||||
bits |= flag.getBits();
|
||||
}
|
||||
return bits;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getData() {
|
||||
AddressRange range = region.getRange();
|
||||
DBTraceMemorySpace space =
|
||||
program.trace.getMemoryManager().getMemorySpace(range.getAddressSpace(), false);
|
||||
if (space == null) {
|
||||
return null;
|
||||
}
|
||||
return new TraceMemorySpaceInputStream(program, space, range);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Address addr) {
|
||||
return region.getRange().contains(addr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address getStart() {
|
||||
return region.getRange().getMinAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address getEnd() {
|
||||
return region.getRange().getMaxAddress();
|
||||
return getAddressRange().contains(addr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSize() {
|
||||
return region.getRange().getLength();
|
||||
return getEnd().subtract(getStart()) + 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return region.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(String name) throws LockException {
|
||||
region.setName(name);
|
||||
public BigInteger getSizeAsBigInteger() {
|
||||
return getEnd().getOffsetAsBigInteger()
|
||||
.subtract(getStart().getOffsetAsBigInteger())
|
||||
.add(BigInteger.ONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getComment() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setComment(String comment) {
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRead() {
|
||||
return region.isRead();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRead(boolean r) {
|
||||
region.setRead(r);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWrite() {
|
||||
return region.isWrite();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWrite(boolean w) {
|
||||
region.setWrite(w);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExecute() {
|
||||
return region.isExecute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExecute(boolean e) {
|
||||
region.setExecute(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVolatile() {
|
||||
return region.isVolatile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVolatile(boolean v) {
|
||||
region.setVolatile(v);
|
||||
public InputStream getData() {
|
||||
DBTraceMemorySpace ms = getMemorySpace();
|
||||
if (ms == null) {
|
||||
return null;
|
||||
}
|
||||
return new TraceMemorySpaceInputStream(program, ms, getAddressRange());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -231,7 +167,7 @@ public class DBTraceProgramViewMemoryBlock implements MemoryBlock {
|
||||
|
||||
@Override
|
||||
public byte getByte(Address addr) throws MemoryAccessException {
|
||||
AddressRange range = region.getRange();
|
||||
AddressRange range = getAddressRange();
|
||||
if (!range.contains(addr)) {
|
||||
throw new MemoryAccessException();
|
||||
}
|
||||
@@ -254,7 +190,7 @@ public class DBTraceProgramViewMemoryBlock implements MemoryBlock {
|
||||
|
||||
@Override
|
||||
public int getBytes(Address addr, byte[] b, int off, int len) throws MemoryAccessException {
|
||||
AddressRange range = region.getRange();
|
||||
AddressRange range = getAddressRange();
|
||||
if (!range.contains(addr)) {
|
||||
throw new MemoryAccessException();
|
||||
}
|
||||
@@ -263,7 +199,7 @@ public class DBTraceProgramViewMemoryBlock implements MemoryBlock {
|
||||
if (space == null) {
|
||||
throw new MemoryAccessException("Space does not exist");
|
||||
}
|
||||
len = (int) Math.min(len, range.getMaxAddress().subtract(addr) + 1);
|
||||
len = MathUtilities.unsignedMin(len, range.getMaxAddress().subtract(addr) + 1);
|
||||
return space.getViewBytes(program.snap, addr, ByteBuffer.wrap(b, off, len));
|
||||
}
|
||||
|
||||
@@ -281,7 +217,7 @@ public class DBTraceProgramViewMemoryBlock implements MemoryBlock {
|
||||
|
||||
@Override
|
||||
public int putBytes(Address addr, byte[] b, int off, int len) throws MemoryAccessException {
|
||||
AddressRange range = region.getRange();
|
||||
AddressRange range = getAddressRange();
|
||||
if (!range.contains(addr)) {
|
||||
throw new MemoryAccessException();
|
||||
}
|
||||
@@ -308,7 +244,8 @@ public class DBTraceProgramViewMemoryBlock implements MemoryBlock {
|
||||
|
||||
@Override
|
||||
public boolean isOverlay() {
|
||||
return false;
|
||||
// TODO: What effect does this have? Does it makes sense for trace "overlays"?
|
||||
return getAddressSpace().isOverlaySpace();
|
||||
}
|
||||
|
||||
@Override
|
||||
+9
@@ -160,6 +160,15 @@ public abstract class AbstractDBTraceProgramViewReferenceManager implements Refe
|
||||
new AddressRangeImpl(fromAddr, fromAddr));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllReferencesTo(Address toAddr) {
|
||||
if (refs(false) == null) {
|
||||
return;
|
||||
}
|
||||
refs.clearReferencesTo(Range.closed(program.snap, program.snap),
|
||||
new AddressRangeImpl(toAddr, toAddr));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Reference[] getReferencesTo(Variable var) {
|
||||
return TODO();
|
||||
|
||||
+31
-13
@@ -943,6 +943,13 @@ public class DBTraceProgramView implements TraceProgramView {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires object-restored event on this view and all associated register views.
|
||||
*/
|
||||
protected void fireObjectRestored() {
|
||||
fireEventAllViews(new DomainObjectChangeRecord(DomainObject.DO_OBJECT_RESTORED));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("<%s on %s at snap=%d>", getClass().getSimpleName(), trace, snap);
|
||||
@@ -1555,52 +1562,63 @@ public class DBTraceProgramView implements TraceProgramView {
|
||||
trace.removeTransactionListener(listener);
|
||||
}
|
||||
|
||||
public void updateMemoryAddBlock(DBTraceMemoryRegion region) {
|
||||
public void updateMemoryAddRegionBlock(DBTraceMemoryRegion region) {
|
||||
if (!isRegionVisible(region)) {
|
||||
return;
|
||||
}
|
||||
memory.updateAddBlock(region);
|
||||
memory.updateAddRegionBlock(region);
|
||||
}
|
||||
|
||||
public void updateMemoryChangeBlockName(DBTraceMemoryRegion region) {
|
||||
public void updateMemoryChangeRegionBlockName(DBTraceMemoryRegion region) {
|
||||
if (!isRegionVisible(region)) {
|
||||
return;
|
||||
}
|
||||
memory.updateChangeBlockName(region);
|
||||
memory.updateChangeRegionBlockName(region);
|
||||
}
|
||||
|
||||
public void updateMemoryChangeBlockFlags(DBTraceMemoryRegion region) {
|
||||
public void updateMemoryChangeRegionBlockFlags(DBTraceMemoryRegion region) {
|
||||
if (!isRegionVisible(region)) {
|
||||
return;
|
||||
}
|
||||
memory.updateChangeBlockFlags(region);
|
||||
memory.updateChangeRegionBlockFlags(region);
|
||||
}
|
||||
|
||||
public void updateMemoryChangeBlockRange(DBTraceMemoryRegion region, AddressRange oldRange,
|
||||
public void updateMemoryChangeRegionBlockRange(DBTraceMemoryRegion region,
|
||||
AddressRange oldRange,
|
||||
AddressRange newRange) {
|
||||
if (!isRegionVisible(region)) {
|
||||
return;
|
||||
}
|
||||
memory.updateChangeBlockRange(region, oldRange, newRange);
|
||||
memory.updateChangeRegionBlockRange(region, oldRange, newRange);
|
||||
}
|
||||
|
||||
public void updateMemoryChangeBlockLifespan(DBTraceMemoryRegion region,
|
||||
public void updateMemoryChangeRegionBlockLifespan(DBTraceMemoryRegion region,
|
||||
Range<Long> oldLifespan, Range<Long> newLifespan) {
|
||||
boolean inOld = isRegionVisible(region, oldLifespan);
|
||||
boolean inNew = isRegionVisible(region, newLifespan);
|
||||
if (inOld && !inNew) {
|
||||
memory.updateDeleteBlock(region);
|
||||
memory.updateDeleteRegionBlock(region);
|
||||
}
|
||||
if (!inOld && inNew) {
|
||||
memory.updateAddBlock(region);
|
||||
memory.updateAddRegionBlock(region);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateMemoryDeleteBlock(DBTraceMemoryRegion region) {
|
||||
public void updateMemoryDeleteRegionBlock(DBTraceMemoryRegion region) {
|
||||
if (!isRegionVisible(region)) {
|
||||
return;
|
||||
}
|
||||
memory.updateAddBlock(region);
|
||||
memory.updateAddRegionBlock(region);
|
||||
}
|
||||
|
||||
public void updateMemoryAddSpaceBlock(AddressSpace space) {
|
||||
// Spaces not not time-bound. No visibility check.
|
||||
memory.updateAddSpaceBlock(space);
|
||||
}
|
||||
|
||||
public void updateMemoryDeleteSpaceBlock(AddressSpace space) {
|
||||
// Spaces not not time-bound. No visibility check.
|
||||
memory.updateDeleteSpaceBlock(space);
|
||||
}
|
||||
|
||||
public void updateMemoryRefreshBlocks() {
|
||||
|
||||
+52
-15
@@ -27,8 +27,19 @@ import ghidra.trace.database.memory.DBTraceMemoryRegion;
|
||||
|
||||
public class DBTraceProgramViewMemory extends AbstractDBTraceProgramViewMemory {
|
||||
|
||||
private final Map<DBTraceMemoryRegion, DBTraceProgramViewMemoryBlock> blocks =
|
||||
CacheBuilder.newBuilder().removalListener(this::blockRemoved).weakValues().build().asMap();
|
||||
// NB. Keep both per-region and force-full (per-space) block sets ready
|
||||
private final Map<DBTraceMemoryRegion, DBTraceProgramViewMemoryRegionBlock> regionBlocks =
|
||||
CacheBuilder.newBuilder()
|
||||
.removalListener(this::regionBlockRemoved)
|
||||
.weakValues()
|
||||
.build()
|
||||
.asMap();
|
||||
private final Map<AddressSpace, DBTraceProgramViewMemorySpaceBlock> spaceBlocks =
|
||||
CacheBuilder.newBuilder()
|
||||
.removalListener(this::spaceBlockRemoved)
|
||||
.weakValues()
|
||||
.build()
|
||||
.asMap();
|
||||
|
||||
public DBTraceProgramViewMemory(DBTraceProgramView program) {
|
||||
super(program);
|
||||
@@ -64,58 +75,84 @@ public class DBTraceProgramViewMemory extends AbstractDBTraceProgramViewMemory {
|
||||
addressSet = temp;
|
||||
}
|
||||
|
||||
protected MemoryBlock getBlock(DBTraceMemoryRegion region) {
|
||||
return blocks.computeIfAbsent(region,
|
||||
r -> new DBTraceProgramViewMemoryBlock(program, region));
|
||||
protected MemoryBlock getRegionBlock(DBTraceMemoryRegion region) {
|
||||
return regionBlocks.computeIfAbsent(region,
|
||||
r -> new DBTraceProgramViewMemoryRegionBlock(program, region));
|
||||
}
|
||||
|
||||
protected MemoryBlock getSpaceBlock(AddressSpace space) {
|
||||
return spaceBlocks.computeIfAbsent(space,
|
||||
s -> new DBTraceProgramViewMemorySpaceBlock(program, space));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemoryBlock getBlock(Address addr) {
|
||||
if (forceFullView) {
|
||||
return getSpaceBlock(addr.getAddressSpace());
|
||||
}
|
||||
DBTraceMemoryRegion region = getTopRegion(s -> memoryManager.getRegionContaining(s, addr));
|
||||
return region == null ? null : getBlock(region);
|
||||
return region == null ? null : getRegionBlock(region);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemoryBlock getBlock(String blockName) {
|
||||
if (forceFullView) {
|
||||
AddressSpace space = program.getAddressFactory().getAddressSpace(blockName);
|
||||
return space == null ? null : getSpaceBlock(space);
|
||||
}
|
||||
DBTraceMemoryRegion region =
|
||||
getTopRegion(s -> memoryManager.getLiveRegionByPath(s, blockName));
|
||||
return region == null ? null : getBlock(region);
|
||||
return region == null ? null : getRegionBlock(region);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemoryBlock[] getBlocks() {
|
||||
List<MemoryBlock> result = new ArrayList<>();
|
||||
forVisibleRegions(reg -> result.add(getBlock(reg)));
|
||||
if (forceFullView) {
|
||||
forPhysicalSpaces(space -> result.add(getSpaceBlock(space)));
|
||||
}
|
||||
else {
|
||||
forVisibleRegions(reg -> result.add(getRegionBlock(reg)));
|
||||
}
|
||||
Collections.sort(result, Comparator.comparing(b -> b.getStart()));
|
||||
return result.toArray(new MemoryBlock[result.size()]);
|
||||
}
|
||||
|
||||
public void updateAddBlock(DBTraceMemoryRegion region) {
|
||||
public void updateAddRegionBlock(DBTraceMemoryRegion region) {
|
||||
// TODO: add block to cache?
|
||||
addRange(region.getRange());
|
||||
}
|
||||
|
||||
public void updateChangeBlockName(DBTraceMemoryRegion region) {
|
||||
public void updateChangeRegionBlockName(DBTraceMemoryRegion region) {
|
||||
// Nothing. Block name is taken from region, uncached
|
||||
}
|
||||
|
||||
public void updateChangeBlockFlags(DBTraceMemoryRegion region) {
|
||||
public void updateChangeRegionBlockFlags(DBTraceMemoryRegion region) {
|
||||
// Nothing. Block flags are taken from region, uncached
|
||||
}
|
||||
|
||||
public void updateChangeBlockRange(DBTraceMemoryRegion region, AddressRange oldRange,
|
||||
public void updateChangeRegionBlockRange(DBTraceMemoryRegion region, AddressRange oldRange,
|
||||
AddressRange newRange) {
|
||||
// TODO: update cached block? Nothing to update.
|
||||
changeRange(oldRange, newRange);
|
||||
}
|
||||
|
||||
public void updateDeleteBlock(DBTraceMemoryRegion region) {
|
||||
blocks.remove(region);
|
||||
public void updateDeleteRegionBlock(DBTraceMemoryRegion region) {
|
||||
regionBlocks.remove(region);
|
||||
removeRange(region.getRange());
|
||||
}
|
||||
|
||||
public void updateAddSpaceBlock(AddressSpace space) {
|
||||
// Nothing. Cache will construct it upon request, lazily
|
||||
}
|
||||
|
||||
public void updateDeleteSpaceBlock(AddressSpace space) {
|
||||
spaceBlocks.remove(space);
|
||||
}
|
||||
|
||||
public void updateRefreshBlocks() {
|
||||
blocks.clear();
|
||||
regionBlocks.clear();
|
||||
spaceBlocks.clear();
|
||||
recomputeAddressSet();
|
||||
}
|
||||
}
|
||||
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
/* ###
|
||||
* 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.database.program;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.math.BigInteger;
|
||||
|
||||
import ghidra.framework.store.LockException;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.trace.database.memory.DBTraceMemoryRegion;
|
||||
import ghidra.trace.database.memory.DBTraceMemorySpace;
|
||||
import ghidra.trace.model.memory.TraceMemoryFlag;
|
||||
import ghidra.trace.model.memory.TraceMemorySpaceInputStream;
|
||||
|
||||
// TODO: Proper locking all over here
|
||||
public class DBTraceProgramViewMemoryRegionBlock extends AbstractDBTraceProgramViewMemoryBlock {
|
||||
|
||||
private final DBTraceMemoryRegion region;
|
||||
|
||||
public DBTraceProgramViewMemoryRegionBlock(DBTraceProgramView program,
|
||||
DBTraceMemoryRegion region) {
|
||||
super(program);
|
||||
this.region = region;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getInfoDescription() {
|
||||
return "Trace region: " + region;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AddressSpace getAddressSpace() {
|
||||
return region.getRange().getAddressSpace();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AddressRange getAddressRange() {
|
||||
return region.getRange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPermissions(boolean read, boolean write, boolean execute) {
|
||||
region.setRead(read);
|
||||
region.setWrite(write);
|
||||
region.setExecute(execute);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPermissions() {
|
||||
int bits = 0;
|
||||
for (TraceMemoryFlag flag : region.getFlags()) {
|
||||
bits |= flag.getBits();
|
||||
}
|
||||
return bits;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getData() {
|
||||
AddressRange range = region.getRange();
|
||||
DBTraceMemorySpace space =
|
||||
program.trace.getMemoryManager().getMemorySpace(range.getAddressSpace(), false);
|
||||
if (space == null) {
|
||||
return null;
|
||||
}
|
||||
return new TraceMemorySpaceInputStream(program, space, range);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address getStart() {
|
||||
return region.getRange().getMinAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address getEnd() {
|
||||
return region.getRange().getMaxAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSize() {
|
||||
return region.getRange().getLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigInteger getSizeAsBigInteger() {
|
||||
return region.getRange().getBigLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return region.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(String name) throws LockException {
|
||||
region.setName(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRead() {
|
||||
return region.isRead();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRead(boolean r) {
|
||||
region.setRead(r);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWrite() {
|
||||
return region.isWrite();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWrite(boolean w) {
|
||||
region.setWrite(w);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExecute() {
|
||||
return region.isExecute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExecute(boolean e) {
|
||||
region.setExecute(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVolatile() {
|
||||
return region.isVolatile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVolatile(boolean v) {
|
||||
region.setVolatile(v);
|
||||
}
|
||||
}
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
/* ###
|
||||
* 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.database.program;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.framework.store.LockException;
|
||||
import ghidra.program.database.mem.ByteMappingScheme;
|
||||
import ghidra.program.database.mem.FileBytes;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.mem.*;
|
||||
import ghidra.trace.database.memory.DBTraceMemorySpace;
|
||||
import ghidra.trace.model.memory.TraceMemorySpaceInputStream;
|
||||
|
||||
public class DBTraceProgramViewMemorySpaceBlock extends AbstractDBTraceProgramViewMemoryBlock {
|
||||
|
||||
private final AddressSpace space;
|
||||
|
||||
public DBTraceProgramViewMemorySpaceBlock(DBTraceProgramView program, AddressSpace space) {
|
||||
super(program);
|
||||
this.space = space;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getInfoDescription() {
|
||||
return "Trace space: " + space;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AddressSpace getAddressSpace() {
|
||||
return space;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address getStart() {
|
||||
return space.getMinAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address getEnd() {
|
||||
return space.getMaxAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPermissions() {
|
||||
return MemoryBlock.READ | MemoryBlock.WRITE | MemoryBlock.EXECUTE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return space.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(String name) throws IllegalArgumentException, LockException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getComment() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setComment(String comment) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRead() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRead(boolean r) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWrite() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWrite(boolean w) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExecute() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExecute(boolean e) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPermissions(boolean read, boolean write, boolean execute) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVolatile() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVolatile(boolean v) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSourceName() {
|
||||
return "Trace"; // TODO: What does this method actually do?
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSourceName(String sourceName) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
+5
@@ -33,6 +33,11 @@ public class DBTraceProgramViewRegisterMemory extends AbstractDBTraceProgramView
|
||||
space.getAddressSpace().getMaxAddress()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setForceFullView(boolean forceFullView) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void recomputeAddressSet() {
|
||||
// AddressSet is always full space
|
||||
|
||||
+6
@@ -16,6 +16,7 @@
|
||||
package ghidra.trace.database.program;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.*;
|
||||
|
||||
@@ -144,6 +145,11 @@ public class DBTraceProgramViewRegisterMemoryBlock implements MemoryBlock {
|
||||
return range.getLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigInteger getSizeAsBigInteger() {
|
||||
return range.getBigLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return REGS_BLOCK_NAME;
|
||||
|
||||
+2
-2
@@ -28,7 +28,6 @@ import ghidra.program.database.map.AddressMap;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
import ghidra.program.model.reloc.RelocationTable;
|
||||
import ghidra.program.model.symbol.*;
|
||||
@@ -40,6 +39,7 @@ import ghidra.trace.database.thread.DBTraceThread;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.data.TraceBasedDataTypeManager;
|
||||
import ghidra.trace.model.program.TraceProgramView;
|
||||
import ghidra.trace.model.program.TraceProgramViewMemory;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.util.TraceTimeViewport;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
@@ -113,7 +113,7 @@ public class DBTraceProgramViewRegisters implements TraceProgramView {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Memory getMemory() {
|
||||
public TraceProgramViewMemory getMemory() {
|
||||
return memory;
|
||||
}
|
||||
|
||||
|
||||
-7
@@ -37,13 +37,6 @@ public class DBTraceVariableSnapProgramView extends DBTraceProgramView
|
||||
super(trace, snap, compilerSpec);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires object-restored event on this view and all associated register views.
|
||||
*/
|
||||
protected void fireObjectRestored() {
|
||||
fireEventAllViews(new DomainObjectChangeRecord(DomainObject.DO_OBJECT_RESTORED));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSnap(long newSnap) {
|
||||
if (this.snap == newSnap) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user