GP-569: Added trace interpolation and extrapolation (emulation)

This commit is contained in:
Dan 2021-01-06 16:04:02 -05:00
parent 57b69005c7
commit fcc0d97ae0
173 changed files with 10969 additions and 1424 deletions

View file

@ -35,10 +35,8 @@ import ghidra.util.Msg;
@TargetObjectSchemaInfo(
name = "Thread",
elements = {
@TargetElementType(type = Void.class) },
attributes = {
@TargetAttributeType(type = Void.class) })
elements = { @TargetElementType(type = Void.class) },
attributes = { @TargetAttributeType(type = Void.class) })
public class GdbModelTargetThread
extends DefaultTargetObject<TargetObject, GdbModelTargetThreadContainer> implements
TargetThread, TargetExecutionStateful, TargetSteppable, GdbModelSelectableObject {

View file

@ -85,6 +85,8 @@ src/main/help/help/topics/DebuggerObjectsPlugin/images/stepinto.png||GHIDRA||||E
src/main/help/help/topics/DebuggerObjectsPlugin/images/stepout.png||GHIDRA||||END|
src/main/help/help/topics/DebuggerObjectsPlugin/images/stepover.png||GHIDRA||||END|
src/main/help/help/topics/DebuggerObjectsPlugin/images/stop.png||GHIDRA||||END|
src/main/help/help/topics/DebuggerPcodeStepperPlugin/DebuggerPcodeStepperPlugin.html||GHIDRA||||END|
src/main/help/help/topics/DebuggerPcodeStepperPlugin/images/DebuggerPcodeStepperPlugin.png||GHIDRA||||END|
src/main/help/help/topics/DebuggerRegionsPlugin/DebuggerRegionsPlugin.html||GHIDRA||||END|
src/main/help/help/topics/DebuggerRegionsPlugin/images/DebuggerRegionsPlugin.png||GHIDRA||||END|
src/main/help/help/topics/DebuggerRegistersPlugin/DebuggerRegistersPlugin.html||GHIDRA||||END|

View file

@ -136,8 +136,12 @@
sortgroup="o"
target="help/topics/DebuggerMemviewPlugin/DebuggerMemviewPlugin.html" />
<tocdef id="DebuggerBots" text="Bots: Workflow Automation"
<tocdef id="DebuggerPcodeStepperPlugin" text="P-code Stepper"
sortgroup="p"
target="help/topics/DebuggerPcodeStepperPlugin/DebuggerPcodeStepperPlugin.html" />
<tocdef id="DebuggerBots" text="Bots: Workflow Automation"
sortgroup="q"
target="help/topics/DebuggerBots/DebuggerBots.html" />
</tocdef>
</tocref>

View file

@ -31,9 +31,9 @@
determines whether both options are in play. For example, threads and inferiors/processes are
both <B>resumable</B>, so the <A href="DebuggerObjectsPlugin.html#resume">Resume</A> action
works on both. For many of our targets, processes are <B>interruptible</B> while threads are
not. Nevertheless, if <B>Enable By Selection Only</B> is off, you can interrupt a thread because
it descends from an inferior or process. In almost every case, the selection directly or
indirectly determines the set of valid or enabled actions.</P>
not. Nevertheless, if <B>Enable By Selection Only</B> is off, you can interrupt a thread
because it descends from an inferior or process. In almost every case, the selection directly
or indirectly determines the set of valid or enabled actions.</P>
<P>The application of these special properties to each object to determine its behavior and
relevant actions allows all objects to be treated generically. This feature has several

View file

@ -0,0 +1,81 @@
<!DOCTYPE doctype PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN">
<HTML>
<HEAD>
<META name="generator" content=
"HTML Tidy for Java (vers. 2009-12-01), see jtidy.sourceforge.net">
<TITLE>Debugger: P-code Stepper</TITLE>
<META http-equiv="Content-Type" content="text/html; charset=windows-1252">
<LINK rel="stylesheet" type="text/css" href="../../shared/Frontpage.css">
</HEAD>
<BODY lang="EN-US">
<H1><A name="plugin"></A>Debugger: P-code Stepper</H1>
<TABLE width="100%">
<TBODY>
<TR>
<TD align="center" width="100%"><IMG alt="" border="1" src=
"images/DebuggerPcodeStepperPlugin.png"></TD>
</TR>
</TBODY>
</TABLE>
<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")
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
similarly to the registers view, displaying the current value of each unique variable.</P>
<P>P-code stepping is built into the emulation framework, and so the other UI elements
(listing, registers, etc.) will display machine state from emulated p-code operations, i.e.,
partially executed machine instructions. The p-code stepper provides a means of navigating time
at p-code-level and displaying p-code-level details of the machine state.</P>
<H2>Table Columns</H2>
<P>The unique variables table displays information about temporary variables, including their
values and user-assigned types. It has the following columns:</P>
<UL>
<LI>Ref - describes how the select p-code operation uses the variable. Blank indicates no
reference. A &larr; indicates read. A &rarr; indicates write.</LI>
<LI>Unique - the name (address and size) of the variable.</LI>
<LI>Bytes - the value displayed as bytes in the machine's endianness.</LI>
<LI>Value - the value displayed in hexadecimal.</LI>
<LI>Type - the user-assigned, ephemeral type of the variable.</LI>
<LI>Representation - the value of the variable as interpreted by its data type.</LI>
</UL>
<H2>Actions</H2>
<P>The p-code stepper provides the following actions:</P>
<H3><A name="step_trace_pcode_backward"></A>Step 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, possibly using emulation. Note
that stepping backward does not affect the target, and many windows that would ordinarily
interact with a live target, may no longer do so, until the user steps back to the present.
Note also that any component or script that does interact with the target and record things
"into the present" may not cause updates in windows that are not displaying the present.</P>
<H3><A name="step_trace_pcode_forward"></A>Step 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, and many
windows that would ordinarily interact with a live target, may not longer do so, until the user
steps back to the present. Note also that any component or script that does interact with the
target and record things "into the present" may not cause updates in windows that are not
displaying the present.</P>
</BODY>
</HTML>

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View file

@ -31,7 +31,7 @@
<H2>Table Columns</H2>
<P>The table displays information about registers, including their values and types. It has the
following columns</P>
following columns:</P>
<UL>
<LI>Favorite - a toggle to mark the register as a favorite. By default this includes the

View file

@ -24,13 +24,14 @@
<P>The stack window displays the current trace's execution stack, as unwound and reported by
the target. Not all debuggers will unwind the stack, in which case, this window displays a
synthetic innermost frame. Level 0 always refers to the innermost frame, and each incremental
synthetic innermost frame. When emulation was used to generate the current machine state, only
a synthetic frame is shown. Level 0 always refers to the innermost frame, and each incremental
level refers to the next caller in the chain &mdash; most of the time. The current frame
comprises one element of the tool's current "coordinates." Selecting a frame changes those
coordinates, potentially causing other windows to display different information. Namely, the <A
href="help/topics/DebuggerRegistersPlugin/DebuggerRegistersPlugin.html">Registers</A> window
will show registers for the current frame, assuming they can be retrieved The Listings may also
navigate to the current frame's program counter.</P>
will show registers for the current frame, assuming they can be retrieved. The Listings may
also navigate to the current frame's program counter.</P>
<H2>Table Columns</H2>

View file

@ -86,25 +86,46 @@
<A href="help/topics/DebuggerTimePlugin/DebuggerTimePlugin.html">Time</A> window for a way to
display and navigate to specific events in the trace's timeline.</P>
<H3><A name="step_trace_backward"></A><IMG alt="" src="images/stepback.png">Step Track
Backward</H3>
<H3><A name="step_trace_snap_backward"></A><IMG alt="" src="images/arrow_up.png">Step Track
Snap Backward</H3>
<P>This action is available when there exists a snapshot previous to the current. It steps the
trace backward to the previous snapshot, causing most windows to display the recorded data from
the new point in time. Note that stepping backward does not affect the target, and many windows
that would ordinarily interact with a live target, may no longer do so, until the user steps
back to the present. Note also that any component or script that does interact with the target
and record things "into the present" will not cause updates in windows that are not displaying
the present.</P>
<H3><A name="step_trace_snap_forward"></A><IMG alt="" src="images/arrow_down.png">Step Trace
Snap Forward</H3>
<P>This action is available when there exists a snapshot ahead of the current. It steps the
trace forward to the next snapshot, causing most windows to display the recorded data from the
new point in time. If the new point in time represents "the present" for a live trace, then
many windows will resume interacting with the target. Note that stepping the trace does not
affect the target; however, stepping back to the present may cause some windows to query the
target.</P>
<H3><A name="step_trace_tick_backward"></A><IMG alt="" src="images/stepback.png">Step Track
Tick Backward</H3>
<P>This action is available when there exists a point in time previous to the current. It steps
the trace backward once, causing most windows to display the recorded data from the new point
in time. Note that stepping backward does not affect the target, and many windows that would
ordinarily interact with a live target, may no longer do so, until the user steps back to the
present. Note also that any component or script that does interact with the target and record
things "into the present" will not cause updates in windows that are not displaying the
present.</P>
the trace backward to the previous tick, possibly using emulation. Note that stepping backward
does not affect the target, and many windows that would ordinarily interact with a live target,
may no longer do so, until the user steps back to the present. Note also that any component or
script that does interact with the target and record things "into the present" may not cause
updates in windows that are not displaying the present.</P>
<H3><A name="step_trace_forward"></A><IMG alt="" src="images/stepinto.png">Step Trace
<H3><A name="step_trace_tick_forward"></A><IMG alt="" src="images/stepinto.png">Step Trace Tick
Forward</H3>
<P>This action is available when there exists a point in time ahead of the current. It steps
the trace forward once, causing most windows to display the recorded data from the new point in
time. If the new point in time represents "the present" for a live trace, then many windows
will resume interacting with the target. Note that stepping the trace does not affect the
target; however, stepping back to the present may cause some windows to query the target.</P>
<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, and many
windows that would ordinarily interact with a live target, may no longer do so, until the user
steps back to the present. Note also that any component or script that does interact with the
target and record things "into the present" may not cause updates in windows that are not
displaying the present.</P>
<H3><A name="seek_trace_present"></A><IMG alt="" src="images/continue.png">Seek Trace to
Present</H3>

View file

@ -24,7 +24,7 @@
<P>This window displays all recorded "snapshots" in the current trace. Typically, there is one
snapshot per event recorded. Other tables often display the times of various events or use time
ranges to describe lifespans of various records. Those times refer to the "Snap," which is a
ranges to describe lifespans of various records. Those times refer to the "snap," which is a
0-up counter of snapshot records. Thus, a snapshot is a collection of observations of a
target's state, usually while suspended, along with any user mark up. Selecting a snapshot
navigates to the selected point in time. Note that browsing the past may prevent other windows
@ -50,5 +50,18 @@
<LI>Description - a user-modifiable description of the snapshot or event. This defaults to
the debugger's description of the event.</LI>
</UL>
<H2>Actions</H2>
<P>The time window provides the following action:</P>
<H3><A name="hide_scratch"></A>Hide Scratch</H3>
<P>This toggle action is always available. It is enabled by default. The emulation service,
which enabled trace extrapolation and interpolation, writes emulated state into the trace's
"scratch space," which comprises all negative snaps. When this toggle is enabled, those
snapshots are hidden. They can be displayed by disabling this toggle. Note that navigating into
scratch space may cause temporary undefined behavior in some windows, and may prevent
interaction with the target.</P>
</BODY>
</HTML>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Before After
Before After

View file

@ -17,6 +17,9 @@ package ghidra.app.plugin.core.debug;
import ghidra.framework.plugintool.*;
/**
* All this really does anymore is handle the auto-service wiring thing
*/
public abstract class AbstractDebuggerPlugin extends Plugin {
@SuppressWarnings("unused")
private AutoService.Wiring autoServiceWiring;

View file

@ -16,6 +16,7 @@
package ghidra.app.plugin.core.debug;
import java.io.IOException;
import java.util.Collection;
import java.util.Objects;
import org.jdom.Element;
@ -30,12 +31,16 @@ 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.util.DefaultTraceTimeViewport;
import ghidra.trace.util.TraceTimeViewport;
import ghidra.util.Msg;
import ghidra.util.NotOwnerException;
public class DebuggerCoordinates {
public static final DebuggerCoordinates NOWHERE =
new DebuggerCoordinates(null, null, null, null, 0L, "", 0) {
new DebuggerCoordinates(null, null, null, null, TraceSchedule.ZERO, 0) {
@Override
public void writeDataState(PluginTool tool, SaveState saveState, String key) {
// Write nothing
@ -47,52 +52,59 @@ public class DebuggerCoordinates {
private static final String KEY_TRACE_PATH = "TracePath";
private static final String KEY_TRACE_VERSION = "TraceVersion";
private static final String KEY_THREAD_KEY = "ThreadKey";
private static final String KEY_SNAP = "Snap";
private static final String KEY_TICKS = "Ticks";
private static final String KEY_TIME = "Time";
private static final String KEY_FRAME = "Frame";
public static DebuggerCoordinates all(Trace trace, TraceRecorder recorder, TraceThread thread,
TraceProgramView view, Long snap, String ticks, Integer frame) {
TraceProgramView view, TraceSchedule time, Integer frame) {
if (trace == NOWHERE.trace && recorder == NOWHERE.recorder && thread == NOWHERE.thread &&
view == NOWHERE.view && snap == NOWHERE.snap && ticks == NOWHERE.ticks &&
frame == NOWHERE.frame) {
view == NOWHERE.view && time == NOWHERE.time && frame == NOWHERE.frame) {
return NOWHERE;
}
return new DebuggerCoordinates(trace, recorder, thread, view, snap, ticks, frame);
return new DebuggerCoordinates(trace, recorder, thread, view, time, frame);
}
public static DebuggerCoordinates trace(Trace trace) {
if (trace == null) {
return NOWHERE;
}
return all(trace, null, null, null, null, null, null);
return all(trace, null, null, null, null, null);
}
public static DebuggerCoordinates recorder(TraceRecorder recorder) {
return all(recorder == null ? null : recorder.getTrace(), recorder,
null, null, recorder == null ? null : recorder.getSnap(), null, null);
null, null, recorder == null ? null : TraceSchedule.snap(recorder.getSnap()), null);
}
public static DebuggerCoordinates thread(TraceThread thread) {
return all(thread == null ? null : thread.getTrace(), null, thread,
null, null, null, null);
null, null, null);
}
public static DebuggerCoordinates view(TraceProgramView view) {
return all(view == null ? null : view.getTrace(), null, null, view,
view == null ? null : view.getSnap(), null, null);
view == null ? null : TraceSchedule.snap(view.getSnap()), null);
}
public static DebuggerCoordinates snap(long snap) {
return all(null, null, null, null, snap, null, null);
return all(null, null, null, null, TraceSchedule.snap(snap), null);
}
public static DebuggerCoordinates time(String time) {
return time(TraceSchedule.parse(time));
}
public static DebuggerCoordinates time(TraceSchedule time) {
return all(null, null, null, null, time, null);
}
public static DebuggerCoordinates frame(int frame) {
return all(null, null, null, null, null, null, frame);
return all(null, null, null, null, null, frame);
}
public static DebuggerCoordinates threadSnap(TraceThread thread, long snap) {
return all(thread == null ? null : thread.getTrace(), null, thread, null, snap, null, null);
return all(thread == null ? null : thread.getTrace(), null, thread, null,
TraceSchedule.snap(snap), null);
}
public static boolean equalsIgnoreRecorderAndView(DebuggerCoordinates a,
@ -103,10 +115,7 @@ public class DebuggerCoordinates {
if (!Objects.equals(a.thread, b.thread)) {
return false;
}
if (!Objects.equals(a.snap, b.snap)) {
return false;
}
if (!Objects.equals(a.ticks, b.ticks)) {
if (!Objects.equals(a.time, b.time)) {
return false;
}
if (!Objects.equals(a.frame, b.frame)) {
@ -119,30 +128,31 @@ public class DebuggerCoordinates {
private final TraceRecorder recorder;
private final TraceThread thread;
private final TraceProgramView view;
private final Long snap;
private final String ticks;
private final TraceSchedule time;
private final Integer frame;
private final int hash;
private Long viewSnap;
private DefaultTraceTimeViewport viewport;
protected DebuggerCoordinates(Trace trace, TraceRecorder recorder, TraceThread thread,
TraceProgramView view, Long snap, String ticks, Integer frame) {
TraceProgramView view, TraceSchedule time, Integer frame) {
this.trace = trace;
this.recorder = recorder;
this.thread = thread;
this.view = view;
this.snap = snap;
this.ticks = ticks;
this.time = time;
this.frame = frame;
this.hash = Objects.hash(trace, recorder, thread, view, snap, ticks, frame);
this.hash = Objects.hash(trace, recorder, thread, view, time, frame);
}
@Override
public String toString() {
return String.format(
"Coords(trace=%s,recorder=%s,thread=%s,view=%s,snap=%d,ticks=%s,frame=%d)",
trace, recorder, thread, view, snap, ticks, frame);
"Coords(trace=%s,recorder=%s,thread=%s,view=%s,time=%s,frame=%d)",
trace, recorder, thread, view, time, frame);
}
@Override
@ -163,10 +173,7 @@ public class DebuggerCoordinates {
if (!Objects.equals(this.view, that.view)) {
return false;
}
if (!Objects.equals(this.snap, that.snap)) {
return false;
}
if (!Objects.equals(this.ticks, that.ticks)) {
if (!Objects.equals(this.time, that.time)) {
return false;
}
if (!Objects.equals(this.frame, that.frame)) {
@ -189,7 +196,7 @@ public class DebuggerCoordinates {
}
public DebuggerCoordinates withRecorder(TraceRecorder newRecorder) {
return all(trace, newRecorder, thread, view, snap, ticks, frame);
return all(trace, newRecorder, thread, view, time, frame);
}
public TraceThread getThread() {
@ -208,7 +215,7 @@ public class DebuggerCoordinates {
}
public DebuggerCoordinates withThread(TraceThread newThread) {
return all(trace, recorder, newThread, view, snap, ticks, frame);
return all(trace, recorder, newThread, view, time, frame);
}
public TraceProgramView getView() {
@ -216,21 +223,60 @@ public class DebuggerCoordinates {
}
public Long getSnap() {
return snap;
return time.getSnap();
}
/**
* Get these same coordinates with time replaced by the given snap-only coordinate
*
* @param newSnap the new snap
* @return the new coordinates
*/
public DebuggerCoordinates withSnap(Long newSnap) {
return all(trace, recorder, thread, view, newSnap, ticks, frame);
return all(trace, recorder, thread, view,
newSnap == null ? time : TraceSchedule.snap(newSnap), frame);
}
public String getTicks() {
return ticks;
public DebuggerCoordinates withTime(TraceSchedule newTime) {
return all(trace, recorder, thread, view, newTime, frame);
}
public TraceSchedule getTime() {
return time;
}
public Integer getFrame() {
return frame;
}
public synchronized long getViewSnap() {
if (viewSnap != null) {
return viewSnap;
}
if (time.isSnapOnly()) {
return viewSnap = time.getSnap();
}
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();
}
return viewSnap = snapshots.iterator().next().getKey();
}
public synchronized TraceTimeViewport getViewport() {
if (viewport != null) {
return viewport;
}
if (trace == null) {
return null;
}
viewport = new DefaultTraceTimeViewport(trace);
viewport.setSnap(getViewSnap());
return viewport;
}
public void writeDataState(PluginTool tool, SaveState saveState, String key) {
SaveState coordState = new SaveState();
// for NOWHERE, key should be completely omitted
@ -252,11 +298,8 @@ public class DebuggerCoordinates {
if (thread != null) {
coordState.putLong(KEY_THREAD_KEY, thread.getKey());
}
if (snap != null) {
coordState.putLong(KEY_SNAP, snap);
}
if (ticks != null) {
coordState.putString(KEY_TICKS, ticks);
if (time != null) {
coordState.putString(KEY_TIME, time.toString());
}
if (frame != null) {
coordState.putInt(KEY_FRAME, frame);
@ -327,37 +370,50 @@ public class DebuggerCoordinates {
long threadKey = coordState.getLong(KEY_THREAD_KEY, 0);
thread = trace.getThreadManager().getThread(threadKey);
}
Long snap = null;
if (coordState.hasValue(KEY_SNAP)) {
snap = coordState.getLong(KEY_SNAP, 0);
String timeSpec = coordState.getString(KEY_TIME, null);
TraceSchedule time;
try {
time = TraceSchedule.parse(timeSpec);
}
catch (Exception e) {
Msg.error(DebuggerCoordinates.class,
"Could not restore invalid time specification: " + timeSpec);
time = TraceSchedule.ZERO;
}
String ticks = coordState.getString(KEY_TICKS, null);
Integer frame = null;
if (coordState.hasValue(KEY_FRAME)) {
frame = coordState.getInt(KEY_FRAME, 0);
}
DebuggerCoordinates coords =
DebuggerCoordinates.all(trace, null, thread, null, snap, ticks, frame);
DebuggerCoordinates.all(trace, null, thread, null, time, frame);
if (!resolve) {
return coords;
}
return traceManager.resolveCoordinates(coords);
}
public boolean isDeadOrPresent() {
return recorder == null || isPresent();
}
public boolean isAlive() {
return recorder != null;
}
public boolean isPresent() {
return recorder.getSnap() == snap;
return recorder.getSnap() == time.getSnap() && time.isSnapOnly();
}
public boolean isReadsPresent() {
return recorder.getSnap() == time.getSnap();
}
public boolean isAliveAndPresent() {
return isAlive() && isPresent();
}
public boolean isDeadOrPresent() {
return !isAlive() || isPresent();
}
public boolean isAliveAndReadsPresent() {
return isAlive() && isReadsPresent();
}
}

View file

@ -35,6 +35,7 @@ import ghidra.app.plugin.core.debug.gui.memory.DebuggerRegionsPlugin;
import ghidra.app.plugin.core.debug.gui.modules.DebuggerModulesPlugin;
import ghidra.app.plugin.core.debug.gui.modules.DebuggerStaticMappingPlugin;
import ghidra.app.plugin.core.debug.gui.objects.DebuggerObjectsPlugin;
import ghidra.app.plugin.core.debug.gui.pcode.DebuggerPcodeStepperPlugin;
import ghidra.app.plugin.core.debug.gui.register.DebuggerRegistersPlugin;
import ghidra.app.plugin.core.debug.gui.stack.DebuggerStackPlugin;
import ghidra.app.plugin.core.debug.gui.target.DebuggerTargetsPlugin;
@ -48,7 +49,9 @@ import ghidra.framework.plugintool.util.PluginUtils;
import ghidra.program.database.ProgramContentHandler;
import ghidra.trace.model.Trace;
import ghidra.util.*;
import resources.MultiIcon;
import resources.ResourceManager;
import resources.icons.RotateIcon;
public interface DebuggerResources {
String OPTIONS_CATEGORY_WORKFLOW = "Debugger.Workflow";
@ -66,16 +69,20 @@ public interface DebuggerResources {
ImageIcon ICON_LAUNCH = ResourceManager.loadImage("images/launch.png");
ImageIcon ICON_ATTACH = ResourceManager.loadImage("images/attach.png");
ImageIcon ICON_RESUME = ResourceManager.loadImage("images/continue.png");
ImageIcon ICON_STEP_INTO = ResourceManager.loadImage("images/stepinto.png");
ImageIcon ICON_STEP_OVER = ResourceManager.loadImage("images/stepover.png");
ImageIcon ICON_STEP_FINISH = ResourceManager.loadImage("images/stepout.png");
ImageIcon ICON_REV_STEP_INTO = ResourceManager.loadImage("images/stepback.png");
ImageIcon ICON_TERMINATE = ResourceManager.loadImage("images/stop.png");
ImageIcon ICON_KILL = ResourceManager.loadImage("images/kill.png");
ImageIcon ICON_DETACH = ResourceManager.loadImage("images/detach.png");
ImageIcon ICON_SEEK_PRESENT = ICON_RESUME; // TODO: Draw a new icon?
ImageIcon ICON_RECORD = ResourceManager.loadImage("images/record.png");
ImageIcon ICON_STEP_INTO = ResourceManager.loadImage("images/stepinto.png");
ImageIcon ICON_STEP_OVER = ResourceManager.loadImage("images/stepover.png");
ImageIcon ICON_STEP_FINISH = ResourceManager.loadImage("images/stepout.png");
ImageIcon ICON_STEP_BACK = ResourceManager.loadImage("images/stepback.png");
// TODO: Draw new icons?
ImageIcon ICON_SNAP_FORWARD = ResourceManager.loadImage("images/2rightarrow.png");
ImageIcon ICON_SNAP_BACKWARD = ResourceManager.loadImage("images/2leftarrow.png");
ImageIcon ICON_SEEK_PRESENT = ICON_RESUME;
ImageIcon ICON_SET_BREAKPOINT = ResourceManager.loadImage("images/breakpoint-set.png");
ImageIcon ICON_CLEAR_BREAKPOINT = ResourceManager.loadImage("images/breakpoint-clear.png");
ImageIcon ICON_ENABLE_BREAKPOINT = ResourceManager.loadImage("images/breakpoint-enable.png");
@ -95,6 +102,7 @@ public interface DebuggerResources {
ImageIcon ICON_BREAKPOINTS = ResourceManager.loadImage("images/breakpoints.png");
ImageIcon ICON_MODULES = ResourceManager.loadImage("images/modules.png");
ImageIcon ICON_MAPPINGS = ICON_PROGRAM; // TODO: A better icon
ImageIcon ICON_PCODE = ResourceManager.loadImage("images/stepinto.png"); // TODO
//ResourceManager.loadImage("images/mappings.png");
ImageIcon ICON_REGIONS = ResourceManager.loadImage("images/memory16.gif");
ImageIcon ICON_TIME = ResourceManager.loadImage("images/time.png");
@ -158,6 +166,11 @@ public interface DebuggerResources {
HelpLocation HELP_PROVIDER_MODULES = new HelpLocation(
PluginUtils.getPluginNameFromClass(DebuggerModulesPlugin.class), HELP_ANCHOR_PLUGIN);
String TITLE_PROVIDER_PCODE = "Pcode Stepper";
ImageIcon ICON_PROVIDER_PCODE = ICON_PCODE;
HelpLocation HELP_PROVIDER_PCODE = new HelpLocation(
PluginUtils.getPluginNameFromClass(DebuggerPcodeStepperPlugin.class), HELP_ANCHOR_PLUGIN);
String TITLE_PROVIDER_REGIONS = "Regions";
ImageIcon ICON_PROVIDER_REGIONS = ICON_REGIONS;
HelpLocation HELP_PROVIDER_REGIONS = new HelpLocation(
@ -233,6 +246,9 @@ public interface DebuggerResources {
String OPTION_NAME_COLORS_WATCH_CHANGED_SEL = "Colors.Changed Watches (selected)";
Color DEFAULT_COLOR_WATCH_CHANGED_SEL = ColorUtils.blend(Color.RED, Color.WHITE, 0.5f);
String OPTION_NAME_COLORS_PCODE_COUNTER = "Colors.Pcode Counter";
Color DEFAULT_COLOR_PCODE_COUNTER = new Color(0.75f, 0.875f, 0.75f);
String MARKER_NAME_BREAKPOINT_ENABLED = "Enabled Breakpoint";
String MARKER_NAME_BREAKPOINT_DISABLED = "Disabled Breakpoint";
String MARKER_NAME_BREAKPOINT_MIXED_ED = "Mixed Enabled-Disabled Breakpont";
@ -248,6 +264,11 @@ public interface DebuggerResources {
ImageIcon ICON_BREAKPOINT_MIXED_DE_MARKER =
ResourceManager.loadImage("images/breakpoint-mixed-de.png");
Icon ICON_UNIQUE_REF_READ =
new RotateIcon(ResourceManager.loadImage("images/cursor_arrow.gif"), 180); // TODO
ImageIcon ICON_UNIQUE_REF_WRITE = ResourceManager.loadImage("images/cursor_arrow.gif"); // TODO
Icon ICON_UNIQUE_REF_RW = new MultiIcon(ICON_UNIQUE_REF_READ, ICON_UNIQUE_REF_WRITE); // TODO
String OPTION_NAME_COLORS_ENABLED_BREAKPOINT_MARKERS = "Colors.Enabled Breakpoint Markers";
Color DEFAULT_COLOR_ENABLED_BREAKPOINT_MARKERS = new Color(0.875f, 0.75f, 0.75f);
String OPTION_NAME_COLORS_DISABLED_BREAKPOINT_MARKERS = "Colors.Disabled Breakpoint Markers";
@ -1088,6 +1109,23 @@ public interface DebuggerResources {
}
}
/*interface SelectAddressesAction { // TODO: Finish this conversion
String NAME = "Select Addresses";
Icon ICON = ICON_SELECT_ADDRESSES;
String GROUP = GROUP_GENERAL;
String HELP_ANCHOR = "select_addresses";
static ActionBuilder builder(Plugin owner) {
String ownerName = owner.getName();
return new ActionBuilder(NAME, ownerName)
.toolBarIcon(ICON)
.toolBarGroup(GROUP)
.popupMenuPath(NAME)
.popupMenuIcon(ICON)
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
}
}*/
abstract class AbstractSelectAddressesAction extends DockingAction {
public static final String NAME = "Select Addresses";
public static final Icon ICON = ICON_SELECT_ADDRESSES;
@ -1146,30 +1184,88 @@ public interface DebuggerResources {
}
}
abstract class AbstractStepTraceForwardAction extends DockingAction {
public static final String NAME = "Step Trace Forward";
abstract class AbstractStepSnapForwardAction extends DockingAction {
public static final String NAME = "Step Trace Snap Forward";
public static final Icon ICON = ICON_SNAP_FORWARD;
public static final String HELP_ANCHOR = "step_trace_snap_forward";
public AbstractStepSnapForwardAction(Plugin owner) {
super(NAME, owner.getName());
setDescription("Navigate the recording forward one snap");
setHelpLocation(new HelpLocation(owner.getName(), HELP_ANCHOR));
}
}
abstract class AbstractStepTickForwardAction extends DockingAction {
public static final String NAME = "Step Trace Tick Forward";
public static final Icon ICON = ICON_STEP_INTO;
public static final String HELP_ANCHOR = "step_trace_forward";
public static final String HELP_ANCHOR = "step_trace_tick_forward";
public AbstractStepTraceForwardAction(Plugin owner) {
public AbstractStepTickForwardAction(Plugin owner) {
super(NAME, owner.getName());
setDescription("Move the recording forward one tick");
setDescription("Navigate the recording forward one tick");
setHelpLocation(new HelpLocation(owner.getName(), HELP_ANCHOR));
}
}
abstract class AbstractStepTraceBackwardAction extends DockingAction {
public static final String NAME = "Step Trace Backward";
public static final Icon ICON = ICON_REV_STEP_INTO;
public static final String HELP_ANCHOR = "step_trace_backward";
interface StepPcodeForwardAction {
String NAME = "Step 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";
public AbstractStepTraceBackwardAction(Plugin owner) {
static ActionBuilder builder(Plugin owner) {
String ownerName = owner.getName();
return new ActionBuilder(NAME, ownerName)
.description(DESCRIPTION)
.toolBarIcon(ICON)
.toolBarGroup(GROUP)
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
}
}
abstract class AbstractStepTickBackwardAction extends DockingAction {
public static final String NAME = "Step Trace Tick Backward";
public static final Icon ICON = ICON_STEP_BACK;
public static final String HELP_ANCHOR = "step_trace_tick_backward";
public AbstractStepTickBackwardAction(Plugin owner) {
super(NAME, owner.getName());
setDescription("Move the recording backward one tick");
setDescription("Navigate the recording backward one tick");
setHelpLocation(new HelpLocation(owner.getName(), HELP_ANCHOR));
}
}
abstract class AbstractStepSnapBackwardAction extends DockingAction {
public static final String NAME = "Step Trace Snap Backward";
public static final Icon ICON = ICON_SNAP_BACKWARD;
public static final String HELP_ANCHOR = "step_trace_snap_backward";
public AbstractStepSnapBackwardAction(Plugin owner) {
super(NAME, owner.getName());
setDescription("Navigate the recording backward one snap");
setHelpLocation(new HelpLocation(owner.getName(), HELP_ANCHOR));
}
}
interface StepPcodeBackwardAction {
String NAME = "Step 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";
static ActionBuilder builder(Plugin owner) {
String ownerName = owner.getName();
return new ActionBuilder(NAME, ownerName)
.description(DESCRIPTION)
.toolBarIcon(ICON)
.toolBarGroup(GROUP)
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
}
}
abstract class AbstractSeekTracePresentAction extends ToggleDockingAction {
public static final String NAME = "Seek Trace Present";
public static final Icon ICON = ICON_SEEK_PRESENT;
@ -1372,6 +1468,22 @@ public interface DebuggerResources {
}
}
interface HideScratchSnapshotsAction {
String NAME = "Hide Scratch";
String DESCRIPTION = "Hide negative snaps, typically used as emulation scratch space";
String GROUP = GROUP_GENERAL;
String HELP_ANCHOR = "hide_scratch";
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() {

View file

@ -104,7 +104,7 @@ public interface DebuggerListingAutoReadMemoryAction extends AutoReadMemoryActio
@Override
public CompletableFuture<Void> readMemory(DebuggerCoordinates coordinates,
AddressSetView visible) {
if (!coordinates.isAliveAndPresent()) {
if (!coordinates.isAliveAndReadsPresent()) {
return AsyncUtils.NIL;
}
TraceRecorder recorder = coordinates.getRecorder();
@ -135,7 +135,7 @@ public interface DebuggerListingAutoReadMemoryAction extends AutoReadMemoryActio
@Override
public CompletableFuture<Void> readMemory(DebuggerCoordinates coordinates,
AddressSetView visible) {
if (!coordinates.isAliveAndPresent()) {
if (!coordinates.isAliveAndReadsPresent()) {
return AsyncUtils.NIL;
}
TraceRecorder recorder = coordinates.getRecorder();

View file

@ -53,40 +53,40 @@ import ghidra.util.Swing;
import utilities.util.SuppressableCallback;
import utilities.util.SuppressableCallback.Suppression;
@PluginInfo(//
shortDescription = "View and annotate listings of trace (possibly live) memory", //
@PluginInfo(
shortDescription = "View and annotate listings of trace (possibly live) memory",
description = "Provides the memory listing display window. Functions similarly to " +
"the main program listing display window, but for traces. If the trace is the " +
"destination of a live recording, the view(s) retrieve live memory on demand.", //
category = PluginCategoryNames.DEBUGGER, //
packageName = DebuggerPluginPackage.NAME, //
status = PluginStatus.RELEASED, //
eventsConsumed = { //
"destination of a live recording, the view(s) retrieve live memory on demand.",
category = PluginCategoryNames.DEBUGGER,
packageName = DebuggerPluginPackage.NAME,
status = PluginStatus.RELEASED,
eventsConsumed = {
// ProgramSelectionPluginEvent.class, // TODO: Later or remove
// ProgramHighlightPluginEvent.class, // TODO: Later or remove
ProgramClosedPluginEvent.class, // For marker set cleanup
ProgramLocationPluginEvent.class, // For static listing sync
TraceActivatedPluginEvent.class, // Trace/thread activation and register tracking
TraceClosedPluginEvent.class, //
}, //
eventsProduced = { //
ProgramLocationPluginEvent.class, //
// ProgramSelectionPluginEvent.class, //
TraceLocationPluginEvent.class, //
TraceSelectionPluginEvent.class //
}, //
servicesRequired = { //
TraceClosedPluginEvent.class,
},
eventsProduced = {
ProgramLocationPluginEvent.class,
// ProgramSelectionPluginEvent.class,
TraceLocationPluginEvent.class,
TraceSelectionPluginEvent.class
},
servicesRequired = {
DebuggerModelService.class, // For memory capture
DebuggerStaticMappingService.class, // For static listing sync. TODO: Optional?
ProgramManager.class, // TODO: Needed?
GoToService.class, // For static listing sync
ClipboardService.class, //
DebuggerEmulationService.class, // TODO: Optional?
ProgramManager.class, // For static listing sync
//GoToService.class, // For static listing sync
ClipboardService.class,
MarkerService.class // TODO: Make optional?
}, //
servicesProvided = { //
DebuggerListingService.class, //
} //
)
},
servicesProvided = {
DebuggerListingService.class,
})
public class DebuggerListingPlugin extends CodeBrowserPlugin implements DebuggerListingService {
private static final String KEY_CONNECTED_PROVIDER = "connectedProvider";
private static final String KEY_DISCONNECTED_COUNT = "disconnectedCount";
@ -118,8 +118,8 @@ public class DebuggerListingPlugin extends CodeBrowserPlugin implements Debugger
protected NewListingAction actionNewListing;
@AutoServiceConsumed
private GoToService goToService;
//@AutoServiceConsumed
//private GoToService goToService;
@AutoServiceConsumed
private ProgramManager programManager;
// NOTE: ListingPlugin doesn't extend AbstractDebuggerPlugin

View file

@ -81,6 +81,7 @@ import ghidra.trace.model.Trace.*;
import ghidra.trace.model.memory.TraceMemoryRegion;
import ghidra.trace.model.modules.*;
import ghidra.trace.model.program.TraceProgramView;
import ghidra.trace.model.program.TraceVariableSnapProgramView;
import ghidra.trace.model.stack.TraceStack;
import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.model.time.TraceSnapshot;
@ -104,8 +105,8 @@ public class DebuggerListingProvider extends CodeViewerProvider implements Listi
if (!Objects.equals(a.getRecorder(), b.getRecorder())) {
return false; // For capture memory action
}
if (!Objects.equals(a.getSnap(), b.getSnap())) {
return false; // Subsumed by view, but I care that it's snap has changed
if (!Objects.equals(a.getTime(), b.getTime())) {
return false;
}
if (!Objects.equals(a.getThread(), b.getThread())) {
return false; // for reg/pc tracking
@ -113,7 +114,6 @@ public class DebuggerListingProvider extends CodeViewerProvider implements Listi
if (!Objects.equals(a.getFrame(), b.getFrame())) {
return false; // for reg/pc tracking
}
// TODO: Ticks
return true;
}
@ -129,14 +129,11 @@ public class DebuggerListingProvider extends CodeViewerProvider implements Listi
@Override
public void actionPerformed(ActionContext context) {
if (current.getView() == null) {
if (!current.isAliveAndReadsPresent()) {
return;
}
Trace trace = current.getTrace();
TraceRecorder recorder = current.getRecorder();
if (recorder == null) {
return;
}
BackgroundUtils.async(plugin.getTool(), trace, NAME, true, true, false,
(__, monitor) -> recorder
.captureProcessMemory(getListingPanel().getProgramSelection(), monitor));
@ -144,13 +141,10 @@ public class DebuggerListingProvider extends CodeViewerProvider implements Listi
@Override
public boolean isEnabledForContext(ActionContext context) {
if (!current.isAliveAndReadsPresent()) {
return false;
}
TraceRecorder recorder = current.getRecorder();
if (recorder == null) {
return false;
}
if (recorder.getSnap() != current.getSnap()) {
return false;
}
ProgramSelection selection = getSelection();
if (selection == null || selection.isEmpty()) {
return false;
@ -334,7 +328,7 @@ public class DebuggerListingProvider extends CodeViewerProvider implements Listi
@SuppressWarnings("unused")
private final AutoService.Wiring autoServiceWiring;
@AutoOptionConsumed(name = OPTION_NAME_COLORS_REGISTER_MARKERS)
@AutoOptionConsumed(name = DebuggerResources.OPTION_NAME_COLORS_REGISTER_MARKERS)
private Color trackingColor;
@SuppressWarnings("unused")
private final AutoOptions.Wiring autoOptionsWiring;
@ -757,7 +751,9 @@ public class DebuggerListingProvider extends CodeViewerProvider implements Listi
return;
}
// Avoid odd race conditions by fixing the snap
TraceProgramView fixed = current.getTrace().getFixedProgramView(current.getSnap());
TraceProgramView fixed = current.getView() instanceof TraceVariableSnapProgramView
? current.getTrace().getFixedProgramView(current.getSnap())
: current.getView();
ExporterDialog dialog =
new ExporterDialog(tool, fixed.getDomainFile(), fixed, getSelection());
@ -1056,13 +1052,14 @@ public class DebuggerListingProvider extends CodeViewerProvider implements Listi
// Change of current snap (TODO)
// Change of current frame (TODO)
// Change of tracking settings
DebuggerCoordinates cur = this.current;
DebuggerCoordinates cur = current;
TraceThread thread = cur.getThread();
if (thread == null || trackingSpec == null) {
return null;
}
Address address = trackingSpec.computeTraceAddress(cur);
return address == null ? null : new ProgramLocation(cur.getView(), address);
// NB: view's snap may be forked for emulation
Address address = trackingSpec.computeTraceAddress(cur, current.getView().getSnap());
return address == null ? null : new ProgramLocation(current.getView(), address);
}
protected ProgramLocation doMarkTrackedLocation() {
@ -1082,16 +1079,16 @@ public class DebuggerListingProvider extends CodeViewerProvider implements Listi
if (loc == null) {
return;
}
DebuggerCoordinates cur = current;
TraceProgramView curView = current.getView();
if (!syncToStaticListing || trackedStatic == null) {
Swing.runIfSwingOrRunLater(() -> {
goTo(cur.getView(), loc);
goTo(curView, loc);
doAutoImportCurrentModule();
});
}
else {
Swing.runIfSwingOrRunLater(() -> {
goTo(cur.getView(), loc);
goTo(curView, loc);
doAutoImportCurrentModule();
plugin.fireStaticLocationEvent(trackedStatic);
});
@ -1118,7 +1115,7 @@ public class DebuggerListingProvider extends CodeViewerProvider implements Listi
}
public void staticProgramLocationChanged(ProgramLocation location) {
TraceProgramView view = current.getView();
TraceProgramView view = current.getView(); // NB. Used for snap (don't want emuSnap)
if (!isSyncToStaticListing() || view == null || location == null) {
return;
}
@ -1134,10 +1131,9 @@ public class DebuggerListingProvider extends CodeViewerProvider implements Listi
return coordinates;
}
// Because the view's snap is changing with or without us.... So go with.
return current.withSnap(coordinates.getSnap());
return current.withTime(coordinates.getTime());
}
// TODO: Figure out clone action
public void goToCoordinates(DebuggerCoordinates coordinates) {
if (sameCoordinates(current, coordinates)) {
current = coordinates;

View file

@ -27,6 +27,7 @@ import ghidra.program.model.lang.RegisterValue;
import ghidra.trace.model.Trace;
import ghidra.trace.model.TraceAddressSnapRange;
import ghidra.trace.model.memory.TraceMemoryRegisterSpace;
import ghidra.trace.model.memory.TraceMemoryState;
import ghidra.trace.model.stack.TraceStack;
import ghidra.trace.model.stack.TraceStackFrame;
import ghidra.trace.model.thread.TraceThread;
@ -90,7 +91,7 @@ public interface DebuggerListingTrackLocationAction extends TrackLocationAction
*/
String computeTitle(DebuggerCoordinates coordinates);
Address computeTraceAddress(DebuggerCoordinates coordinates);
Address computeTraceAddress(DebuggerCoordinates coordinates, long emuSnap);
// TODO: Is there a way to generalize these so that other dependencies need not
// have their own bespoke methods?
@ -115,7 +116,7 @@ public interface DebuggerListingTrackLocationAction extends TrackLocationAction
}
@Override
public Address computeTraceAddress(DebuggerCoordinates coordinates) {
public Address computeTraceAddress(DebuggerCoordinates coordinates, long emuSnap) {
return null;
}
@ -147,7 +148,7 @@ public interface DebuggerListingTrackLocationAction extends TrackLocationAction
}
@Override
default Address computeTraceAddress(DebuggerCoordinates coordinates) {
default Address computeTraceAddress(DebuggerCoordinates coordinates, long emuSnap) {
Trace trace = coordinates.getTrace();
TraceThread thread = coordinates.getThread();
long snap = coordinates.getSnap();
@ -164,7 +165,13 @@ public interface DebuggerListingTrackLocationAction extends TrackLocationAction
if (regs == null) {
return null;
}
RegisterValue value = regs.getValue(snap, reg);
RegisterValue value;
if (regs.getState(emuSnap, reg) == TraceMemoryState.KNOWN) {
value = regs.getValue(emuSnap, reg);
}
else {
value = regs.getValue(snap, reg);
}
if (value == null) {
return null;
}
@ -230,12 +237,14 @@ public interface DebuggerListingTrackLocationAction extends TrackLocationAction
}
@Override
public Address computeTraceAddress(DebuggerCoordinates coordinates) {
public Address computeTraceAddress(DebuggerCoordinates coordinates, long emuSnap) {
if (coordinates.getTime().isSnapOnly()) {
Address pc = computePCViaStack(coordinates);
if (pc != null) {
return pc;
}
return RegisterLocationTrackingSpec.super.computeTraceAddress(coordinates);
}
return RegisterLocationTrackingSpec.super.computeTraceAddress(coordinates, emuSnap);
}
// Note it does no good to override affectByRegChange. It must do what we'd avoid anyway.
@ -244,6 +253,9 @@ public interface DebuggerListingTrackLocationAction extends TrackLocationAction
if (stack.getThread() != coordinates.getThread()) {
return false;
}
if (!coordinates.getTime().isSnapOnly()) {
return false;
}
// TODO: Would be nice to have stack lifespan...
TraceStack curStack = coordinates.getTrace()
.getStackManager()

View file

@ -63,11 +63,11 @@ public class MemoryStateListingBackgroundColorModel implements ListingBackground
return defaultBackgroundColor;
}
TraceMemoryState state = memory.getState(view.getSnap(), address);
Entry<Long, TraceMemoryState> state = memory.getViewState(view.getSnap(), address);
if (state == null) {
return defaultBackgroundColor;
}
switch (state) {
switch (state.getValue()) {
case UNKNOWN:
return getUnknownColor(address);
case ERROR:
@ -84,11 +84,11 @@ public class MemoryStateListingBackgroundColorModel implements ListingBackground
protected Color getUnknownColor(Address address) {
Entry<TraceAddressSnapRange, TraceMemoryState> ent =
memory.getMostRecentStateEntry(view.getSnap(), address);
memory.getViewMostRecentStateEntry(view.getSnap(), address);
if (ent == null || ent.getValue() != TraceMemoryState.KNOWN) {
return unknownColor;
}
TraceMemoryRegion region = memory.getRegionContaining(view.getSnap(), address);
TraceMemoryRegion region = memory.getRegionContaining(ent.getKey().getY1(), address);
if (region != null && !region.isWrite()) {
return unknownBlendedColor;
}

View file

@ -490,9 +490,6 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter {
}
}
// TODO: Display modules in timeline?
// TODO: Select module/section containing current address?
private final DebuggerModulesPlugin plugin;
// @AutoServiceConsumed via method

View file

@ -0,0 +1,43 @@
/* ###
* 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.app.plugin.core.debug.gui.pcode;
import ghidra.program.model.pcode.PcodeOp;
public class BranchPcodeRow implements PcodeRow {
private final int sequence;
private final int fromSeq;
public BranchPcodeRow(int sequence, int fromSeq) {
this.sequence = sequence;
this.fromSeq = fromSeq;
}
@Override
public int getSequence() {
return sequence;
}
@Override
public String getCode() {
return "(branched from " + fromSeq + ")";
}
@Override
public PcodeOp getOp() {
return null;
}
}

View file

@ -0,0 +1,61 @@
/* ###
* 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.app.plugin.core.debug.gui.pcode;
import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.plugin.core.debug.AbstractDebuggerPlugin;
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
import ghidra.app.plugin.core.debug.event.TraceActivatedPluginEvent;
import ghidra.app.services.DebuggerEmulationService;
import ghidra.app.services.DebuggerTraceManagerService;
import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.util.PluginStatus;
@PluginInfo(
shortDescription = "Debugger p-code stepper",
description = "GUI to single-step emulation of p-code",
category = PluginCategoryNames.DEBUGGER,
packageName = DebuggerPluginPackage.NAME,
status = PluginStatus.UNSTABLE,
eventsConsumed = {
TraceActivatedPluginEvent.class,
},
servicesRequired = {
DebuggerTraceManagerService.class,
DebuggerEmulationService.class,
})
public class DebuggerPcodeStepperPlugin extends AbstractDebuggerPlugin {
protected DebuggerPcodeStepperProvider provider;
public DebuggerPcodeStepperPlugin(PluginTool tool) {
super(tool);
}
@Override
protected void init() {
provider = new DebuggerPcodeStepperProvider(this);
super.init();
}
@Override
public void processEvent(PluginEvent event) {
super.processEvent(event);
if (event instanceof TraceActivatedPluginEvent) {
TraceActivatedPluginEvent ev = (TraceActivatedPluginEvent) event;
provider.coordinatesActivated(ev.getActiveCoordinates());
}
}
}

View file

@ -0,0 +1,679 @@
/* ###
* 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.app.plugin.core.debug.gui.pcode;
import java.awt.*;
import java.math.BigInteger;
import java.util.*;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.swing.*;
import javax.swing.table.*;
import org.apache.commons.lang3.StringUtils;
import docking.action.DockingAction;
import docking.widgets.table.*;
import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn;
import ghidra.GhidraOptions;
import ghidra.app.plugin.core.debug.DebuggerCoordinates;
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
import ghidra.app.plugin.core.debug.gui.pcode.UniqueRow.RefType;
import ghidra.app.plugin.core.debug.service.emulation.DebuggerTracePcodeEmulator;
import ghidra.app.services.DebuggerEmulationService;
import ghidra.app.services.DebuggerTraceManagerService;
import ghidra.async.SwingExecutorService;
import ghidra.base.widgets.table.DataTypeTableCellEditor;
import ghidra.docking.settings.Settings;
import ghidra.framework.options.AutoOptions;
import ghidra.framework.options.annotation.*;
import ghidra.framework.plugintool.AutoService;
import ghidra.framework.plugintool.ComponentProviderAdapter;
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
import ghidra.pcode.emu.PcodeThread;
import ghidra.pcode.exec.PcodeExecutorState;
import ghidra.pcode.exec.PcodeFrame;
import ghidra.program.model.data.DataType;
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.util.ColorUtils;
import ghidra.util.HTMLUtilities;
import ghidra.util.database.UndoableTransaction;
import ghidra.util.table.GhidraTable;
import ghidra.util.table.GhidraTableFilterPanel;
import ghidra.util.table.column.AbstractGColumnRenderer;
public class DebuggerPcodeStepperProvider extends ComponentProviderAdapter {
private static final String BACKGROUND_COLOR = "Background Color";
private static final String ADDRESS_COLOR = "Address Color";
private static final String CONSTANT_COLOR = "Constant Color";
private static final String REGISTERS_COLOR = "Registers Color";
private static final String LABELS_LOCAL_COLOR = "Labels, Local Color";
private static final String MNEMONIC_COLOR = "Mnemonic Color";
protected static final Comparator<Varnode> UNIQUE_COMPARATOR = (u1, u2) -> {
assert u1.isUnique() && u2.isUnique();
return u1.getAddress().compareTo(u2.getAddress());
};
protected enum PcodeTableColumns implements EnumeratedTableColumn<PcodeTableColumns, PcodeRow> {
SEQUENCE("Sequence", Integer.class, PcodeRow::getSequence),
CODE("Code", String.class, PcodeRow::getCode);
private final String header;
private final Function<PcodeRow, ?> getter;
private final Class<?> cls;
<T> PcodeTableColumns(String header, Class<T> cls, Function<PcodeRow, T> getter) {
this.header = header;
this.cls = cls;
this.getter = getter;
}
@Override
public String getHeader() {
return header;
}
@Override
public Class<?> getValueClass() {
return cls;
}
@Override
public Object getValueOf(PcodeRow row) {
return getter.apply(row);
}
@Override
public boolean isSortable() {
return this == SEQUENCE; // HACK
}
}
protected static class PcodeTableModel
extends DefaultEnumeratedColumnTableModel<PcodeTableColumns, PcodeRow> {
public PcodeTableModel() {
super("p-code", PcodeTableColumns.class);
}
@Override
public List<PcodeTableColumns> defaultSortOrder() {
return List.of(PcodeTableColumns.SEQUENCE);
}
}
protected enum UniqueTableColumns
implements EnumeratedTableColumn<UniqueTableColumns, UniqueRow> {
REF("Ref", RefType.class, UniqueRow::getRefType),
UNIQUE("Unique", String.class, UniqueRow::getName),
BYTES("Bytes", String.class, UniqueRow::getBytes),
VALUE("Value", BigInteger.class, UniqueRow::getValue),
TYPE("Type", DataType.class, UniqueRow::getDataType, UniqueRow::setDataType),
REPR("Repr", String.class, UniqueRow::getValueRepresentation);
private final String header;
private final Function<UniqueRow, ?> getter;
private final BiConsumer<UniqueRow, Object> setter;
private final Class<?> cls;
@SuppressWarnings("unchecked")
<T> UniqueTableColumns(String header, Class<T> cls, Function<UniqueRow, T> getter,
BiConsumer<UniqueRow, T> setter) {
this.header = header;
this.cls = cls;
this.getter = getter;
this.setter = (BiConsumer<UniqueRow, Object>) setter;
}
<T> UniqueTableColumns(String header, Class<T> cls, Function<UniqueRow, T> getter) {
this(header, cls, getter, null);
}
@Override
public Class<?> getValueClass() {
return cls;
}
@Override
public Object getValueOf(UniqueRow row) {
return getter.apply(row);
}
@Override
public String getHeader() {
return header;
}
@Override
public void setValueOf(UniqueRow row, Object value) {
setter.accept(row, value);
}
@Override
public boolean isEditable(UniqueRow row) {
return setter != null;
}
}
protected static class UniqueTableModel
extends DefaultEnumeratedColumnTableModel<UniqueTableColumns, UniqueRow> {
public UniqueTableModel() {
super("Unique", UniqueTableColumns.class);
}
@Override
public List<UniqueTableColumns> defaultSortOrder() {
return List.of(UniqueTableColumns.UNIQUE);
}
}
class UniqueDataTypeEditor extends DataTypeTableCellEditor {
public UniqueDataTypeEditor() {
super(plugin.getTool());
}
@Override
protected DataType resolveSelection(DataType dataType) {
if (dataType == null) {
return null;
}
try (UndoableTransaction tid =
UndoableTransaction.start(current.getTrace(), "Resolve DataType", true)) {
return current.getTrace().getDataTypeManager().resolve(dataType, null);
}
}
}
class CounterBackgroundCellRenderer extends AbstractGColumnRenderer<String> {
Color foregroundColor = getForeground();
@Override
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
super.getTableCellRendererComponent(data);
setForeground(pcodeTable.getForeground());
boolean isCurrent = counter == data.getRowModelIndex();
if (data.isSelected()) {
if (isCurrent) {
setBackground(ColorUtils.blend(counterColor, cursorColor, 0.5f));
}
// else background is already set. Leave it alone
}
else if (isCurrent) {
setBackground(counterColor);
}
else {
setBackground(pcodeTable.getBackground());
setOpaque(true);
}
setBorder(noFocusBorder);
return this;
}
@Override
public String getFilterString(String t, Settings settings) {
return t;
}
}
class PcodeCellRenderer extends CounterBackgroundCellRenderer {
{
setHTMLRenderingEnabled(true);
}
@Override
protected void configureFont(JTable table, TableModel model, int column) {
setFont(fixedWidthFont);
}
@Override
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
super.getTableCellRendererComponent(data);
setText(injectStyle(getText()));
return this;
}
String injectStyle(String html) {
if (StringUtils.startsWithIgnoreCase(html, "<html>")) {
return style + html.substring("<html>".length());
}
return html;
}
}
class UniqueRefCellRenderer extends AbstractGColumnRenderer<RefType> {
@Override
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
super.getTableCellRendererComponent(data);
setText("");
switch ((RefType) data.getValue()) {
case NONE:
setIcon(null);
break;
case READ:
setIcon(DebuggerResources.ICON_UNIQUE_REF_READ);
break;
case WRITE:
setIcon(DebuggerResources.ICON_UNIQUE_REF_WRITE);
break;
case READ_WRITE:
setIcon(DebuggerResources.ICON_UNIQUE_REF_RW);
break;
default:
throw new AssertionError();
}
return this;
}
@Override
public String getFilterString(RefType t, Settings settings) {
return t.name();
}
}
protected static String createColoredStyle(String cls, Color color) {
if (color == null) {
return "";
}
return " ." + cls + " { color:" + HTMLUtilities.toHexString(color) + "; }";
}
protected static boolean sameCoordinates(DebuggerCoordinates a, DebuggerCoordinates b) {
if (!Objects.equals(a.getTrace(), b.getTrace())) {
return false;
}
if (!Objects.equals(a.getTime(), b.getTime())) {
return false;
}
if (!Objects.equals(a.getThread(), b.getThread())) {
return false;
}
return true;
}
private final DebuggerPcodeStepperPlugin plugin;
DebuggerCoordinates current = DebuggerCoordinates.NOWHERE;
DebuggerCoordinates previous = DebuggerCoordinates.NOWHERE;
int counter;
@AutoServiceConsumed
private DebuggerTraceManagerService traceManager;
@AutoServiceConsumed // NB. also by method
private DebuggerEmulationService emulationService;
@SuppressWarnings("unused")
private AutoService.Wiring autoServiceWiring;
@AutoOptionDefined(
name = DebuggerResources.OPTION_NAME_COLORS_PCODE_COUNTER,
description = "Background color for the current p-code operation",
help = @HelpInfo(anchor = "colors"))
private Color counterColor = DebuggerResources.DEFAULT_COLOR_PCODE_COUNTER;
private Color backgroundColor;
private Color cursorColor;
private Color addressColor;
private Color constantColor;
private Color registerColor;
private Color uniqueColor;
private Color opColor;
@SuppressWarnings("unused")
private AutoOptions.Wiring autoOptionsWiring;
String style = "<html>";
JSplitPane mainPanel = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
GhidraTable uniqueTable;
UniqueTableModel uniqueTableModel = new UniqueTableModel();
private GhidraTableFilterPanel<UniqueRow> uniqueFilterPanel;
GhidraTable pcodeTable;
PcodeTableModel pcodeTableModel = new PcodeTableModel();
// No filter panel on p-code
DockingAction actionStepBackward;
DockingAction actionStepForward;
public DebuggerPcodeStepperProvider(DebuggerPcodeStepperPlugin plugin) {
super(plugin.getTool(), DebuggerResources.TITLE_PROVIDER_PCODE, plugin.getName(), null);
this.plugin = plugin;
this.autoServiceWiring = AutoService.wireServicesConsumed(plugin, this);
this.autoOptionsWiring = AutoOptions.wireOptions(plugin, this);
setIcon(DebuggerResources.ICON_PROVIDER_PCODE);
setHelpLocation(DebuggerResources.HELP_PROVIDER_PCODE);
setWindowMenuGroup(DebuggerPluginPackage.NAME);
buildMainPanel();
createActions();
setVisible(true);
contextChanged();
}
@AutoOptionConsumed(
name = DebuggerResources.OPTION_NAME_COLORS_PCODE_COUNTER)
private void setCounterColor() {
pcodeTableModel.fireTableDataChanged();
}
@AutoOptionConsumed(
category = GhidraOptions.CATEGORY_BROWSER_DISPLAY,
name = BACKGROUND_COLOR)
private void setBackgroundColor(Color backgroundColor) {
this.backgroundColor = backgroundColor;
if (pcodeTable != null) {
pcodeTable.setBackground(backgroundColor);
}
}
@AutoOptionConsumed(
category = GhidraOptions.CATEGORY_BROWSER_FIELDS,
name = GhidraOptions.HIGHLIGHT_CURSOR_LINE_COLOR)
private void setCursorColor(Color cursorColor) {
this.cursorColor = cursorColor;
if (pcodeTable != null) {
pcodeTable.setSelectionBackground(cursorColor);
}
}
@AutoOptionConsumed(
category = GhidraOptions.CATEGORY_BROWSER_DISPLAY,
name = ADDRESS_COLOR)
private void setAddressColor(Color addressColor) {
this.addressColor = addressColor;
recomputeStyle();
}
@AutoOptionConsumed(
category = GhidraOptions.CATEGORY_BROWSER_DISPLAY,
name = CONSTANT_COLOR)
private void setConstantColor(Color constantColor) {
this.constantColor = constantColor;
recomputeStyle();
}
@AutoOptionConsumed(
category = GhidraOptions.CATEGORY_BROWSER_DISPLAY,
name = REGISTERS_COLOR)
private void setRegisterColor(Color registerColor) {
this.registerColor = registerColor;
recomputeStyle();
}
@AutoOptionConsumed(
category = GhidraOptions.CATEGORY_BROWSER_DISPLAY,
name = LABELS_LOCAL_COLOR)
private void setUniqueColor(Color uniqueColor) {
this.uniqueColor = uniqueColor;
recomputeStyle();
}
@AutoOptionConsumed(
category = GhidraOptions.CATEGORY_BROWSER_DISPLAY,
name = MNEMONIC_COLOR)
private void setOpColor(Color opColor) {
this.opColor = opColor;
recomputeStyle();
}
protected void recomputeStyle() {
StringBuilder sb = new StringBuilder("<html><head><style>");
sb.append(createColoredStyle("address", addressColor));
sb.append(createColoredStyle("constant", constantColor));
sb.append(createColoredStyle("register", registerColor));
sb.append(createColoredStyle("unique", uniqueColor));
sb.append(createColoredStyle("op", opColor));
sb.append("</style></head>"); // NB. </html> should already be at end
style = sb.toString();
pcodeTableModel.fireTableDataChanged();
}
protected void buildMainPanel() {
JPanel pcodePanel = new JPanel(new BorderLayout());
pcodeTable = new GhidraTable(pcodeTableModel);
pcodePanel.add(new JScrollPane(pcodeTable));
mainPanel.setLeftComponent(pcodePanel);
JPanel uniquePanel = new JPanel(new BorderLayout());
uniqueTable = new GhidraTable(uniqueTableModel);
uniquePanel.add(new JScrollPane(uniqueTable));
uniqueFilterPanel = new GhidraTableFilterPanel<>(uniqueTable, uniqueTableModel);
uniquePanel.add(uniqueFilterPanel, BorderLayout.SOUTH);
mainPanel.setRightComponent(uniquePanel);
pcodeTable.setTableHeader(null);
pcodeTable.setBackground(backgroundColor);
pcodeTable.setSelectionBackground(cursorColor);
pcodeTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
pcodeTable.getSelectionModel().addListSelectionListener(evt -> {
if (evt.getValueIsAdjusting()) {
return;
}
uniqueTableModel.fireTableDataChanged();
});
TableColumnModel pcodeColModel = pcodeTable.getColumnModel();
TableColumn seqCol = pcodeColModel.getColumn(PcodeTableColumns.SEQUENCE.ordinal());
seqCol.setCellRenderer(new CounterBackgroundCellRenderer());
seqCol.setMinWidth(24);
seqCol.setMaxWidth(24);
TableColumn codeCol = pcodeColModel.getColumn(PcodeTableColumns.CODE.ordinal());
codeCol.setCellRenderer(new PcodeCellRenderer());
//codeCol.setPreferredWidth(75);
TableColumnModel uniqueColModel = uniqueTable.getColumnModel();
TableColumn refCol = uniqueColModel.getColumn(UniqueTableColumns.REF.ordinal());
refCol.setCellRenderer(new UniqueRefCellRenderer());
refCol.setMinWidth(24);
refCol.setMaxWidth(24);
TableColumn uniqCol = uniqueColModel.getColumn(UniqueTableColumns.UNIQUE.ordinal());
uniqCol.setPreferredWidth(45);
TableColumn bytesCol = uniqueColModel.getColumn(UniqueTableColumns.BYTES.ordinal());
bytesCol.setCellRenderer(CustomToStringCellRenderer.MONO_OBJECT);
bytesCol.setPreferredWidth(65);
TableColumn valCol = uniqueColModel.getColumn(UniqueTableColumns.VALUE.ordinal());
valCol.setCellRenderer(CustomToStringCellRenderer.MONO_BIG_HEX); // TODO: Changed coloring
valCol.setPreferredWidth(45);
TableColumn typeCol = uniqueColModel.getColumn(UniqueTableColumns.TYPE.ordinal());
typeCol.setCellEditor(new UniqueDataTypeEditor());
typeCol.setPreferredWidth(45);
TableColumn reprCol = uniqueColModel.getColumn(UniqueTableColumns.REPR.ordinal());
reprCol.setPreferredWidth(45);
}
protected void createActions() {
actionStepBackward = DebuggerResources.StepPcodeBackwardAction.builder(plugin)
.enabledWhen(c -> current.getTrace() != null && current.getTime().pTickCount() != 0)
.onAction(c -> stepBackwardActivated())
.buildAndInstallLocal(this);
actionStepForward = DebuggerResources.StepPcodeForwardAction.builder(plugin)
.enabledWhen(
c -> current.getThread() != null)
.onAction(c -> stepForwardActivated())
.buildAndInstallLocal(this);
}
private void stepBackwardActivated() {
if (current.getTrace() == null) {
return;
}
TraceSchedule time = current.getTime().steppedPcodeBackward(1);
if (time == null) {
return;
}
traceManager.activateTime(time);
}
private void stepForwardActivated() {
if (current.getThread() == null) {
return;
}
TraceSchedule time = current.getTime().steppedPcodeForward(current.getThread(), 1);
traceManager.activateTime(time);
}
@Override
public JComponent getComponent() {
return mainPanel;
}
public void coordinatesActivated(DebuggerCoordinates coordinates) {
if (sameCoordinates(current, coordinates)) {
current = coordinates;
return;
}
previous = current;
current = coordinates;
doLoadPcodeFrame();
setSubTitle(current.getTime().toString());
contextChanged();
}
protected void populateSingleton(PcodeRow row) {
counter = 0;
pcodeTableModel.clear();
pcodeTableModel.add(row);
uniqueTableModel.clear();
}
protected void populateFromFrame(PcodeFrame frame, PcodeExecutorState<byte[]> state) {
populatePcode(frame);
populateUnique(frame, state);
}
protected void populatePcode(PcodeFrame frame) {
Language language = current.getTrace().getBaseLanguage();
int index = frame.index();
List<PcodeRow> toAdd = frame.getCode()
.stream()
.map(op -> new OpPcodeRow(language, op, index == op.getSeqnum().getTime()))
.collect(Collectors.toCollection(ArrayList::new));
if (frame.isBranch()) {
counter = toAdd.size();
toAdd.add(new BranchPcodeRow(counter, frame.getBranched()));
}
else if (frame.isFallThrough()) {
counter = toAdd.size();
toAdd.add(new FallthroughPcodeRow(counter));
}
else {
counter = index;
}
pcodeTableModel.clear();
pcodeTableModel.addAll(toAdd);
pcodeTable.getSelectionModel().setSelectionInterval(counter, counter);
pcodeTable.scrollToSelectedRow();
}
protected void populateUnique(PcodeFrame frame, PcodeExecutorState<byte[]> state) {
Language language = current.getTrace().getBaseLanguage();
// NOTE: They may overlap. I don't think I care.
Set<Varnode> uniques = new TreeSet<>(UNIQUE_COMPARATOR);
for (PcodeOp op : frame.getCode()) {
Varnode out = op.getOutput();
if (out != null && out.isUnique()) {
uniques.add(out);
}
for (Varnode in : op.getInputs()) {
if (in.isUnique()) {
uniques.add(in);
}
}
}
// TODO: Highlight uniques that the selected op(s) reference
// (including overlaps)
// TODO: Permit modification of unique variables
List<UniqueRow> toAdd =
uniques.stream()
.map(u -> new UniqueRow(this, language, state, u))
.collect(Collectors.toList());
uniqueTableModel.clear();
uniqueTableModel.addAll(toAdd);
}
protected void doLoadPcodeFrame() {
if (emulationService == null) {
return;
}
DebuggerCoordinates current = this.current; // Volatile, also after background
Trace trace = current.getTrace();
if (trace == null) {
return;
}
if (current.getThread() == null) {
populateSingleton(EnumPcodeRow.NO_THREAD);
return;
}
TraceSchedule time = current.getTime();
if (time.pTickCount() == 0) {
populateSingleton(EnumPcodeRow.DECODE);
return;
}
DebuggerTracePcodeEmulator emu = emulationService.getCachedEmulator(trace, time);
if (emu != null) {
doLoadPcodeFrameFromEmulator(emu);
return;
}
emulationService.backgroundEmulate(trace, time).thenAcceptAsync(__ -> {
if (current != this.current) {
return;
}
doLoadPcodeFrameFromEmulator(emulationService.getCachedEmulator(trace, time));
}, SwingExecutorService.INSTANCE);
}
protected void doLoadPcodeFrameFromEmulator(DebuggerTracePcodeEmulator emu) {
PcodeThread<byte[]> thread = emu.getThread(current.getThread().getPath(), false);
if (thread == null) {
/**
* Happens when focus is on a thread not stepped in the schedule. Stepping it would
* create it and decode its first instruction.
*/
populateSingleton(EnumPcodeRow.DECODE);
return;
}
PcodeFrame frame = thread.getFrame();
if (frame == null) {
/**
* Happens when an instruction is completed via p-code stepping, but the next
* instruction has not been decoded, yet.
*/
populateSingleton(EnumPcodeRow.DECODE);
return;
}
populateFromFrame(frame, thread.getState());
}
@AutoServiceConsumed
private void setEmulationService(DebuggerEmulationService emulationService) {
doLoadPcodeFrame();
}
}

View file

@ -0,0 +1,43 @@
/* ###
* 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.app.plugin.core.debug.gui.pcode;
import ghidra.program.model.pcode.PcodeOp;
public enum EnumPcodeRow implements PcodeRow {
NO_THREAD("no thread selected"), DECODE("decode instruction");
private final String message;
private EnumPcodeRow(String message) {
this.message = "(" + message + ")";
}
@Override
public int getSequence() {
return 0;
}
@Override
public String getCode() {
return message;
}
@Override
public PcodeOp getOp() {
return null;
}
}

View file

@ -0,0 +1,41 @@
/* ###
* 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.app.plugin.core.debug.gui.pcode;
import ghidra.program.model.pcode.PcodeOp;
public class FallthroughPcodeRow implements PcodeRow {
private final int sequence;
public FallthroughPcodeRow(int sequence) {
this.sequence = sequence;
}
@Override
public int getSequence() {
return sequence;
}
@Override
public String getCode() {
return "(fall-through)";
}
@Override
public PcodeOp getOp() {
return null;
}
}

View file

@ -0,0 +1,51 @@
/* ###
* 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.app.plugin.core.debug.gui.pcode;
import ghidra.pcode.exec.PcodeProgram;
import ghidra.program.model.lang.Language;
import ghidra.program.model.pcode.PcodeOp;
public class OpPcodeRow implements PcodeRow {
protected final Language language;
protected final PcodeOp op;
protected final boolean isNext;
public OpPcodeRow(Language language, PcodeOp op, boolean isNext) {
this.language = language;
this.op = op;
this.isNext = isNext;
}
@Override
public int getSequence() {
return op.getSeqnum().getTime();
}
@Override
public String getCode() {
return "<html>" + PcodeProgram.opToString(language, op, true) + "</html>";
}
public boolean isNext() {
return isNext;
}
@Override
public PcodeOp getOp() {
return op;
}
}

View file

@ -0,0 +1,26 @@
/* ###
* 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.app.plugin.core.debug.gui.pcode;
import ghidra.program.model.pcode.PcodeOp;
public interface PcodeRow {
int getSequence();
String getCode();
PcodeOp getOp();
}

View file

@ -0,0 +1,146 @@
/* ###
* 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.app.plugin.core.debug.gui.pcode;
import java.math.BigInteger;
import java.util.stream.Stream;
import ghidra.docking.settings.SettingsImpl;
import ghidra.pcode.exec.PcodeExecutorState;
import ghidra.pcode.exec.PcodeProgram;
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.mem.ByteMemBufferImpl;
import ghidra.program.model.mem.MemBuffer;
import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.pcode.Varnode;
import ghidra.util.NumericUtilities;
public class UniqueRow {
public enum RefType {
NONE, READ, WRITE, READ_WRITE;
static RefType fromRW(boolean isRead, boolean isWrite) {
if (isRead) {
if (isWrite) {
return READ_WRITE;
}
else {
return READ;
}
}
else {
if (isWrite) {
return WRITE;
}
else {
return NONE;
}
}
}
}
protected final DebuggerPcodeStepperProvider provider;
protected final Language language;
protected final PcodeExecutorState<byte[]> state;
protected final Varnode vn;
protected DataType dataType;
public UniqueRow(DebuggerPcodeStepperProvider provider, Language language,
PcodeExecutorState<byte[]> state, Varnode vn) {
if (!vn.isUnique()) {
throw new AssertionError("Only uniques allowed in unique table");
}
this.provider = provider;
this.language = language;
this.state = state;
this.vn = vn;
}
protected static AddressRange rangeOf(Varnode vn) {
try {
return new AddressRangeImpl(vn.getAddress(), vn.getSize());
}
catch (AddressOverflowException e) {
throw new AssertionError(e);
}
}
protected static boolean overlap(Varnode vn1, Varnode vn2) {
return rangeOf(vn1).intersects(rangeOf(vn2));
}
public RefType getRefType() {
int index = provider.pcodeTable.getSelectedRow();
if (index == -1) {
return RefType.NONE;
}
PcodeRow row = provider.pcodeTableModel.getRowObject(index);
PcodeOp op = row.getOp();
if (op == null) {
return RefType.NONE;
}
boolean isRead = Stream.of(op.getInputs()).anyMatch(in -> overlap(in, vn));
Varnode out = op.getOutput();
boolean isWrite = out != null && overlap(out, vn);
return RefType.fromRW(isRead, isWrite);
}
public String getName() {
return PcodeProgram.uniqueToString(vn, false);
}
public String getBytes() {
// TODO: Could keep value cached?
byte[] bytes = state.getVar(vn);
if (bytes == null) {
return "??";
}
if (bytes.length > 20) {
return NumericUtilities.convertBytesToString(bytes, 0, 20, " ") + " ...";
}
return NumericUtilities.convertBytesToString(bytes, " ");
}
public BigInteger getValue() {
byte[] bytes = state.getVar(vn);
return Utils.bytesToBigInteger(bytes, bytes.length, language.isBigEndian(), false);
}
public DataType getDataType() {
return dataType;
}
public void setDataType(DataType dataType) {
this.dataType = dataType;
}
public String getValueRepresentation() {
// TODO: Could compute this upon setting data type?
if (dataType == null) {
return "";
}
byte[] bytes = state.getVar(vn);
if (bytes == null) {
return "??";
}
MemBuffer buffer = new ByteMemBufferImpl(vn.getAddress(), bytes, language.isBigEndian());
return dataType.getRepresentation(buffer, SettingsImpl.NO_SETTINGS, bytes.length);
}
}

View file

@ -35,22 +35,22 @@ import ghidra.program.util.DefaultLanguageService;
import ghidra.trace.model.Trace;
import ghidra.util.Msg;
@PluginInfo( //
shortDescription = "Debugger registers manager", //
description = "GUI to view and modify register values", //
category = PluginCategoryNames.DEBUGGER, //
packageName = DebuggerPluginPackage.NAME, //
status = PluginStatus.RELEASED, //
eventsConsumed = { TraceActivatedPluginEvent.class, //
TraceClosedPluginEvent.class, //
}, //
servicesRequired = { //
DebuggerModelService.class, //
DebuggerTraceManagerService.class, //
@PluginInfo(
shortDescription = "Debugger registers manager",
description = "GUI to view and modify register values",
category = PluginCategoryNames.DEBUGGER,
packageName = DebuggerPluginPackage.NAME,
status = PluginStatus.RELEASED,
eventsConsumed = {
TraceActivatedPluginEvent.class,
TraceClosedPluginEvent.class,
},
servicesRequired = {
DebuggerModelService.class,
DebuggerTraceManagerService.class,
MarkerService.class, // TODO
DataTypeManagerService.class, // For DataType selection field
} //
)
})
public class DebuggerRegistersPlugin extends AbstractDebuggerPlugin {
private static final String KEY_SELECTION_BY_CSPEC = "selectionByCSpec";
private static final String KEY_FAVORITES_BY_CSPEC = "favoritesByCSpec";

View file

@ -70,9 +70,9 @@ import ghidra.trace.model.Trace.*;
import ghidra.trace.model.listing.*;
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.util.TraceAddressSpace;
import ghidra.trace.util.TraceRegisterUtils;
import ghidra.trace.util.*;
import ghidra.util.Msg;
import ghidra.util.Swing;
import ghidra.util.data.DataTypeParser.AllowedDataTypes;
@ -171,13 +171,12 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
if (!Objects.equals(a.getThread(), b.getThread())) {
return false;
}
if (!Objects.equals(a.getSnap(), b.getSnap())) {
if (!Objects.equals(a.getTime(), b.getTime())) {
return false;
}
if (!Objects.equals(a.getFrame(), b.getFrame())) {
return false;
}
// TODO: Ticks
return true;
}
@ -185,6 +184,7 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
public TraceChangeListener() {
listenForUntyped(DomainObject.DO_OBJECT_RESTORED, e -> objectRestored(e));
listenFor(TraceMemoryBytesChangeType.CHANGED, this::registerValueChanged);
listenFor(TraceMemoryStateChangeType.CHANGED, this::registerStateChanged);
listenFor(TraceCodeChangeType.ADDED, this::registerTypeAdded);
listenFor(TraceCodeChangeType.DATA_TYPE_REPLACED, this::registerTypeReplaced);
listenFor(TraceCodeChangeType.LIFESPAN_CHANGED, this::registerTypeLifespanChanged);
@ -211,9 +211,11 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
if (!isVisible(space)) {
return false;
}
if (!range.getLifespan().contains(current.getSnap())) {
TraceProgramView view = current.getView();
if (view == null || !view.getViewport().containsAnyUpper(range.getLifespan())) {
return false;
}
// Probably not worth checking for occlusion here. Just a little refresh waste.
return true;
}
@ -238,13 +240,21 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
refreshRange(range.getRange());
}
private void registerStateChanged(TraceAddressSpace space, TraceAddressSnapRange range,
TraceMemoryState oldState, TraceMemoryState newState) {
if (!isVisible(space, range)) {
return;
}
recomputeViewKnown();
refreshRange(range.getRange());
}
private void registerTypeAdded(TraceAddressSpace space, TraceAddressSnapRange range,
TraceCodeUnit oldIsNull, TraceCodeUnit newUnit) {
if (!isVisible(space, range)) {
return;
}
refreshRange(range.getRange());
}
private void registerTypeReplaced(TraceAddressSpace space, TraceAddressSnapRange range,
@ -260,10 +270,15 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
if (!isVisible(space)) {
return;
}
long snap = current.getSnap();
if (oldSpan.contains(snap) == newSpan.contains(snap)) {
TraceProgramView view = current.getView();
if (view == null) {
return;
}
TraceTimeViewport viewport = view.getViewport();
if (viewport.containsAnyUpper(oldSpan) == viewport.containsAnyUpper(newSpan)) {
return;
}
// A little waste if occluded, but probably cheaper than checking.
AddressRange range = new AddressRangeImpl(unit.getMinAddress(), unit.getMaxAddress());
refreshRange(range); // Slightly wasteful, as we already have the data unit
}
@ -418,6 +433,7 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
DockingAction actionClearDataType;
DebuggerRegisterActionContext myActionContext;
AddressSetView viewKnown;
protected DebuggerRegistersProvider(final DebuggerRegistersPlugin plugin,
Map<CompilerSpec, LinkedHashSet<Register>> selectionByCSpec,
@ -710,6 +726,7 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
doSetRecorder(current.getRecorder());
updateSubTitle();
recomputeViewKnown();
loadRegistersAndValues();
contextChanged();
//checkEditsEnabled();
@ -725,16 +742,10 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
}
boolean canWriteTarget() {
if (!current.isAliveAndPresent()) {
return false;
}
TraceRecorder recorder = current.getRecorder();
if (recorder == null) {
return false;
}
if (!recorder.isRecording()) {
return false;
}
if (recorder.getSnap() != current.getSnap()) {
return false;
}
TargetRegisterBank targetRegs =
recorder.getTargetRegisterBank(current.getThread(), current.getFrame());
if (targetRegs == null) {
@ -760,8 +771,7 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
if (regs == null) {
return BigInteger.ZERO;
}
long snap = current.getSnap();
return regs.getValue(snap, register).getUnsignedValue();
return regs.getViewValue(current.getViewSnap(), register).getUnsignedValue();
}
void writeRegisterValue(Register register, BigInteger value) {
@ -840,15 +850,24 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
return TraceRegisterUtils.getValueRepresentationHackPointer(data);
}
boolean isRegisterKnown(Register register) {
void recomputeViewKnown() {
TraceMemoryRegisterSpace regs = getRegisterMemorySpace(false);
if (regs == null) {
TraceProgramView view = current.getView();
if (regs == null || view == null) {
viewKnown = null;
return;
}
viewKnown = new AddressSet(view.getViewport()
.unionedAddresses(snap -> regs.getAddressesWithState(snap,
state -> state == TraceMemoryState.KNOWN)));
}
boolean isRegisterKnown(Register register) {
if (viewKnown == null) {
return false;
}
AddressRange range = TraceRegisterUtils.rangeForRegister(register);
return regs
.getAddressesWithState(current.getSnap(), state -> state == TraceMemoryState.KNOWN)
.contains(range.getMinAddress(), range.getMaxAddress());
return viewKnown.contains(range.getMinAddress(), range.getMaxAddress());
}
boolean isRegisterChanged(Register register) {
@ -861,20 +880,14 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
if (!isRegisterKnown(register)) {
return false;
}
TraceMemoryRegisterSpace curVals = getRegisterMemorySpace(current, false);
TraceMemoryRegisterSpace prevVals = getRegisterMemorySpace(previous, false);
if (prevVals == null) {
TraceMemoryRegisterSpace curSpace = getRegisterMemorySpace(current, false);
TraceMemoryRegisterSpace prevSpace = getRegisterMemorySpace(previous, false);
if (prevSpace == null) {
return false;
}
// is register known at previous? // Do I care?
/*AddressRange range = TraceRegisterUtils.rangeForRegister(register);
if (!prevVals
.getAddressesWithState(previous.getSnap(), state -> state == TraceMemoryState.KNOWN)
.contains(range.getMinAddress(), range.getMaxAddress())) {
return false;
}*/
return !Objects.equals(curVals.getValue(current.getSnap(), register),
prevVals.getValue(previous.getSnap(), register));
RegisterValue curRegVal = curSpace.getViewValue(current.getViewSnap(), register);
RegisterValue prevRegVal = prevSpace.getViewValue(previous.getViewSnap(), register);
return !Objects.equals(curRegVal, prevRegVal);
}
private boolean computeEditsEnabled() {

View file

@ -41,13 +41,15 @@ import ghidra.program.model.address.Address;
import ghidra.program.model.lang.Register;
import ghidra.program.model.lang.RegisterValue;
import ghidra.program.model.listing.Program;
import ghidra.trace.model.Trace;
import ghidra.trace.model.*;
import ghidra.trace.model.Trace.TraceMemoryBytesChangeType;
import ghidra.trace.model.Trace.TraceStackChangeType;
import ghidra.trace.model.TraceDomainObjectListener;
import ghidra.trace.model.memory.TraceMemoryRegisterSpace;
import ghidra.trace.model.stack.TraceStack;
import ghidra.trace.model.stack.TraceStackFrame;
import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.util.TraceAddressSpace;
import ghidra.trace.util.TraceRegisterUtils;
import ghidra.util.Swing;
import ghidra.util.table.GhidraTable;
import ghidra.util.table.GhidraTableFilterPanel;
@ -129,13 +131,12 @@ public class DebuggerStackProvider extends ComponentProviderAdapter {
if (!Objects.equals(a.getThread(), b.getThread())) {
return false;
}
if (!Objects.equals(a.getSnap(), b.getSnap())) {
if (!Objects.equals(a.getTime(), b.getTime())) {
return false;
}
if (!Objects.equals(a.getFrame(), b.getFrame())) {
return false;
}
// TODO: Ticks
return true;
}
@ -144,9 +145,14 @@ public class DebuggerStackProvider extends ComponentProviderAdapter {
listenFor(TraceStackChangeType.ADDED, this::stackAdded);
listenFor(TraceStackChangeType.CHANGED, this::stackChanged);
listenFor(TraceStackChangeType.DELETED, this::stackDeleted);
listenFor(TraceMemoryBytesChangeType.CHANGED, this::bytesChanged);
}
private void stackAdded(TraceStack stack) {
if (stack.getSnap() != current.getViewSnap()) {
return;
}
TraceThread curThread = current.getThread();
if (curThread != stack.getThread()) {
return;
@ -167,6 +173,35 @@ public class DebuggerStackProvider extends ComponentProviderAdapter {
}
loadStack();
}
// For updating a synthetic frame
private void bytesChanged(TraceAddressSpace space, TraceAddressSnapRange range) {
TraceThread curThread = current.getThread();
if (space.getThread() != curThread || space.getFrameLevel() != 0) {
return;
}
if (!current.getView().getViewport().containsAnyUpper(range.getLifespan())) {
return;
}
List<StackFrameRow> stackData = stackTableModel.getModelData();
if (stackData.isEmpty() || !(stackData.get(0) instanceof StackFrameRow.Synthetic)) {
return;
}
StackFrameRow.Synthetic frameRow = (StackFrameRow.Synthetic) stackData.get(0);
Trace trace = current.getTrace();
Register pc = trace.getBaseLanguage().getProgramCounter();
if (!TraceRegisterUtils.rangeForRegister(pc).intersects(range.getRange())) {
return;
}
TraceMemoryRegisterSpace regs =
trace.getMemoryManager().getMemoryRegisterSpace(curThread, false);
RegisterValue value = regs.getViewValue(current.getViewSnap(), pc);
Address address = trace.getBaseLanguage()
.getDefaultSpace()
.getAddress(value.getUnsignedValue().longValue());
frameRow.updateProgramCounter(address);
stackTableModel.fireTableDataChanged();
}
}
class ForFunctionsListener implements DebuggerStaticMappingChangeListener {
@ -367,7 +402,7 @@ public class DebuggerStackProvider extends ComponentProviderAdapter {
return;
}
Register pc = curTrace.getBaseLanguage().getProgramCounter();
RegisterValue value = regs.getValue(current.getSnap(), pc);
RegisterValue value = regs.getViewValue(current.getViewSnap(), pc);
if (value == null) {
contextChanged();
return;
@ -375,7 +410,7 @@ public class DebuggerStackProvider extends ComponentProviderAdapter {
Address address = curTrace.getBaseLanguage()
.getDefaultSpace()
.getAddress(value.getUnsignedValue().longValue());
stackTableModel.add(new StackFrameRow(this, address));
stackTableModel.add(new StackFrameRow.Synthetic(this, address));
}
protected void loadStack() {
@ -384,8 +419,9 @@ public class DebuggerStackProvider extends ComponentProviderAdapter {
doSetCurrentStack(null);
return;
}
// TODO: getLatestViewStack? Conventionally, I don't expect any scratch stacks, yet.
TraceStack stack =
current.getTrace().getStackManager().getLatestStack(curThread, current.getSnap());
current.getTrace().getStackManager().getLatestStack(curThread, current.getViewSnap());
if (stack == null) {
doSetSyntheticStack();
}

View file

@ -27,11 +27,21 @@ import ghidra.trace.model.thread.TraceThread;
import ghidra.util.database.UndoableTransaction;
public class StackFrameRow {
public static class Synthetic extends StackFrameRow {
public Synthetic(DebuggerStackProvider provider, Address pc) {
super(provider, pc);
}
public void updateProgramCounter(Address pc) {
this.pc = pc;
}
}
private final DebuggerStackProvider provider;
final TraceStackFrame frame;
private int level;
private Address pc;
Address pc;
public StackFrameRow(DebuggerStackProvider provider, TraceStackFrame frame) {
this.provider = provider;
@ -40,7 +50,7 @@ public class StackFrameRow {
this.pc = frame.getProgramCounter();
}
public StackFrameRow(DebuggerStackProvider provider, Address pc) {
private StackFrameRow(DebuggerStackProvider provider, Address pc) {
this.provider = provider;
this.frame = null;
this.level = 0;

View file

@ -49,6 +49,7 @@ 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.util.Swing;
import ghidra.util.datastruct.CollectionChangeListener;
@ -73,34 +74,40 @@ public class DebuggerThreadsProvider extends ComponentProviderAdapter {
if (!Objects.equals(a.getThread(), b.getThread())) {
return false;
}
if (!Objects.equals(a.getSnap(), b.getSnap())) {
if (!Objects.equals(a.getTime(), b.getTime())) {
return false;
}
// TODO: Ticks
return true;
}
protected class StepTraceBackwardAction extends AbstractStepTraceBackwardAction {
public static final String GROUP = DebuggerResources.GROUP_GENERAL;
protected class StepSnapBackwardAction extends AbstractStepSnapBackwardAction {
public static final String GROUP = DebuggerResources.GROUP_CONTROL;
public StepTraceBackwardAction() {
public StepSnapBackwardAction() {
super(plugin);
setToolBarData(new ToolBarData(ICON, GROUP));
setToolBarData(new ToolBarData(ICON, GROUP, "1"));
addLocalAction(this);
setEnabled(false);
}
@Override
public void actionPerformed(ActionContext context) {
if (current.getTime().isSnapOnly()) {
traceManager.activateSnap(current.getSnap() - 1);
}
else {
traceManager.activateSnap(current.getSnap());
}
}
@Override
public boolean isEnabledForContext(ActionContext context) {
if (current.getTrace() == null) {
return false;
}
// TODO: Can I track minSnap?
if (!current.getTime().isSnapOnly()) {
return true;
}
if (current.getSnap() <= 0) {
return false;
}
@ -108,12 +115,80 @@ public class DebuggerThreadsProvider extends ComponentProviderAdapter {
}
}
protected class StepTraceForwardAction extends AbstractStepTraceForwardAction {
public static final String GROUP = DebuggerResources.GROUP_GENERAL;
protected class StepTickBackwardAction extends AbstractStepTickBackwardAction {
public static final String GROUP = DebuggerResources.GROUP_CONTROL;
public StepTraceForwardAction() {
public StepTickBackwardAction() {
super(plugin);
setToolBarData(new ToolBarData(ICON, GROUP));
setToolBarData(new ToolBarData(ICON, GROUP, "2"));
addLocalAction(this);
setEnabled(false);
}
@Override
public void actionPerformed(ActionContext context) {
if (current.getTrace() == null) {
return;
}
TraceSchedule time = current.getTime().steppedBackward(current.getTrace(), 1);
if (time == null) {
return;
}
traceManager.activateTime(time);
}
@Override
public boolean isEnabledForContext(ActionContext context) {
if (emulationService == null) {
return false;
}
if (current.getTrace() == null) {
return false;
}
if (current.getTime().steppedBackward(current.getTrace(), 1) == null) {
return false;
}
return true;
}
}
protected class StepTickForwardAction extends AbstractStepTickForwardAction {
public static final String GROUP = DebuggerResources.GROUP_CONTROL;
public StepTickForwardAction() {
super(plugin);
setToolBarData(new ToolBarData(ICON, GROUP, "3"));
addLocalAction(this);
setEnabled(false);
}
@Override
public void actionPerformed(ActionContext context) {
if (current.getThread() == null) {
return;
}
TraceSchedule time = current.getTime().steppedForward(current.getThread(), 1);
traceManager.activateTime(time);
}
@Override
public boolean isEnabledForContext(ActionContext context) {
if (emulationService == null) {
return false;
}
if (current.getThread() == null) {
return false;
}
return true;
}
}
protected class StepSnapForwardAction extends AbstractStepSnapForwardAction {
public static final String GROUP = DebuggerResources.GROUP_CONTROL;
public StepSnapForwardAction() {
super(plugin);
setToolBarData(new ToolBarData(ICON, GROUP, "4"));
addLocalAction(this);
setEnabled(false);
}
@ -139,7 +214,7 @@ public class DebuggerThreadsProvider extends ComponentProviderAdapter {
protected class SeekTracePresentAction extends AbstractSeekTracePresentAction
implements BooleanChangeAdapter {
public static final String GROUP = DebuggerResources.GROUP_GENERAL;
public static final String GROUP = "zz";
public SeekTracePresentAction() {
super(plugin);
@ -241,6 +316,8 @@ public class DebuggerThreadsProvider extends ComponentProviderAdapter {
private DebuggerModelService modelService;
@AutoServiceConsumed // NB, also by method
private DebuggerTraceManagerService traceManager;
@AutoServiceConsumed // NB, also by method
private DebuggerEmulationService emulationService;
@SuppressWarnings("unused")
private final AutoService.Wiring autoWiring;
@ -269,8 +346,10 @@ public class DebuggerThreadsProvider extends ComponentProviderAdapter {
private ActionContext myActionContext;
DockingAction actionSaveTrace;
StepTraceBackwardAction actionStepTraceBackward;
StepTraceForwardAction actionStepTraceForward;
StepSnapBackwardAction actionStepSnapBackward;
StepTickBackwardAction actionStepTickBackward;
StepTickForwardAction actionStepTickForward;
StepSnapForwardAction actionStepSnapForward;
SeekTracePresentAction actionSeekTracePresent;
ToggleDockingAction actionSyncFocus;
Set<Object> strongRefs = new HashSet<>(); // Eww
@ -323,6 +402,11 @@ public class DebuggerThreadsProvider extends ComponentProviderAdapter {
contextChanged();
}
@AutoServiceConsumed
public void setEmulationService(DebuggerEmulationService emulationService) {
contextChanged();
}
private void removeOldListeners() {
if (currentTrace == null) {
return;
@ -394,6 +478,9 @@ public class DebuggerThreadsProvider extends ComponentProviderAdapter {
doSetTrace(current.getTrace());
doSetThread(current.getThread());
doSetSnap(current.getSnap());
setSubTitle(current.getTime().toString());
contextChanged();
}
@ -568,8 +655,10 @@ public class DebuggerThreadsProvider extends ComponentProviderAdapter {
protected void createActions() {
// TODO: Make other actions use builder?
actionStepTraceBackward = new StepTraceBackwardAction();
actionStepTraceForward = new StepTraceForwardAction();
actionStepSnapBackward = new StepSnapBackwardAction();
actionStepTickBackward = new StepTickBackwardAction();
actionStepTickForward = new StepTickForwardAction();
actionStepSnapForward = new StepSnapForwardAction();
actionSeekTracePresent = new SeekTracePresentAction();
actionSyncFocus = SynchronizeFocusAction.builder(plugin)
.selected(traceManager != null && traceManager.isSynchronizeFocus())

View file

@ -20,6 +20,7 @@ import ghidra.app.plugin.core.debug.AbstractDebuggerPlugin;
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
import ghidra.app.plugin.core.debug.event.TraceActivatedPluginEvent;
import ghidra.app.services.DebuggerTraceManagerService;
import ghidra.framework.options.SaveState;
import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.util.PluginStatus;
@ -63,4 +64,14 @@ public class DebuggerTimePlugin extends AbstractDebuggerPlugin {
provider.coordinatesActivated(ev.getActiveCoordinates());
}
}
@Override
public void writeConfigState(SaveState saveState) {
provider.writeConfigState(saveState);
}
@Override
public void readConfigState(SaveState saveState) {
provider.readConfigState(saveState);
}
}

View file

@ -19,6 +19,8 @@ import static ghidra.app.plugin.core.debug.gui.DebuggerResources.*;
import java.awt.BorderLayout;
import java.awt.event.MouseEvent;
import java.lang.invoke.MethodHandles;
import java.util.Collection;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.Function;
@ -31,16 +33,19 @@ import com.google.common.collect.Collections2;
import docking.ActionContext;
import docking.action.DockingActionIf;
import docking.action.ToggleDockingAction;
import docking.widgets.table.*;
import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn;
import ghidra.app.plugin.core.debug.DebuggerCoordinates;
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
import ghidra.app.plugin.core.debug.gui.DebuggerSnapActionContext;
import ghidra.app.services.DebuggerTraceManagerService;
import ghidra.framework.model.DomainObject;
import ghidra.framework.plugintool.AutoService;
import ghidra.framework.options.SaveState;
import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.AutoService.Wiring;
import ghidra.framework.plugintool.ComponentProviderAdapter;
import ghidra.framework.plugintool.annotation.AutoConfigStateField;
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
import ghidra.trace.model.Trace;
import ghidra.trace.model.Trace.TraceSnapshotChangeType;
@ -50,13 +55,15 @@ import ghidra.trace.model.time.TraceTimeManager;
import ghidra.util.table.GhidraTableFilterPanel;
public class DebuggerTimeProvider extends ComponentProviderAdapter {
private static final AutoConfigState.ClassHandler<DebuggerTimeProvider> CONFIG_STATE_HANDLER =
AutoConfigState.wireHandler(DebuggerTimeProvider.class, MethodHandles.lookup());
protected enum SnapshotTableColumns
implements EnumeratedTableColumn<SnapshotTableColumns, SnapshotRow> {
SNAP("Snap", Long.class, SnapshotRow::getSnap),
TIMESTAMP("Timestamp", String.class, SnapshotRow::getTimeStamp), // TODO: Use Date type here
EVENT_THREAD("Event Thread", String.class, SnapshotRow::getEventThreadName),
TICKS("Ticks", Long.class, SnapshotRow::getTicks),
SCHEDULE("Schedule", String.class, SnapshotRow::getSchedule),
DESCRIPTION("Description", String.class, SnapshotRow::getDescription, SnapshotRow::setDescription);
private final String header;
@ -103,17 +110,6 @@ public class DebuggerTimeProvider extends ComponentProviderAdapter {
}
}
protected static boolean sameCoordinates(DebuggerCoordinates a, DebuggerCoordinates b) {
if (!Objects.equals(a.getTrace(), b.getTrace())) {
return false;
}
if (!Objects.equals(a.getSnap(), b.getSnap())) {
return false;
}
// TODO: Ticks?
return true;
}
private class SnapshotListener extends TraceDomainObjectListener {
public SnapshotListener() {
listenForUntyped(DomainObject.DO_OBJECT_RESTORED, e -> objectRestored());
@ -128,6 +124,9 @@ public class DebuggerTimeProvider extends ComponentProviderAdapter {
}
private void snapAdded(TraceSnapshot snapshot) {
if (snapshot.getKey() < 0 && hideScratch) {
return;
}
SnapshotRow row = new SnapshotRow(current.getTrace(), snapshot);
snapshotTableModel.add(row);
if (current.getSnap() == snapshot.getKey()) {
@ -136,14 +135,30 @@ public class DebuggerTimeProvider extends ComponentProviderAdapter {
}
private void snapChanged(TraceSnapshot snapshot) {
if (snapshot.getKey() < 0 && hideScratch) {
return;
}
snapshotTableModel.notifyUpdatedWith(row -> row.getSnapshot() == snapshot);
}
private void snapDeleted(TraceSnapshot snapshot) {
if (snapshot.getKey() < 0 && hideScratch) {
return;
}
snapshotTableModel.deleteWith(row -> row.getSnapshot() == snapshot);
}
}
protected static boolean sameCoordinates(DebuggerCoordinates a, DebuggerCoordinates b) {
if (!Objects.equals(a.getTrace(), b.getTrace())) {
return false;
}
if (!Objects.equals(a.getTime(), b.getTime())) {
return false;
}
return true;
}
protected final DebuggerTimePlugin plugin;
DebuggerCoordinates current = DebuggerCoordinates.NOWHERE;
@ -163,7 +178,12 @@ public class DebuggerTimeProvider extends ComponentProviderAdapter {
/* testing */ GTable snapshotTable;
/* testing */ GhidraTableFilterPanel<SnapshotRow> snapshotFilterPanel;
private DebuggerSnapActionContext currentCtx;
private DebuggerSnapActionContext myActionContext;
ToggleDockingAction actionHideScratch;
@AutoConfigStateField
/* testing */ boolean hideScratch = true;
public DebuggerTimeProvider(DebuggerTimePlugin plugin) {
super(plugin.getTool(), TITLE_PROVIDER_TIME, plugin.getName());
@ -177,6 +197,11 @@ public class DebuggerTimeProvider extends ComponentProviderAdapter {
setWindowMenuGroup(DebuggerPluginPackage.NAME);
buildMainPanel();
myActionContext = new DebuggerSnapActionContext(0);
createActions();
contextChanged();
setVisible(true);
}
@ -192,7 +217,10 @@ public class DebuggerTimeProvider extends ComponentProviderAdapter {
@Override
public ActionContext getActionContext(MouseEvent event) {
return currentCtx;
if (myActionContext == null) {
return super.getActionContext(event);
}
return myActionContext;
}
protected void buildMainPanel() {
@ -209,15 +237,16 @@ public class DebuggerTimeProvider extends ComponentProviderAdapter {
}
SnapshotRow row = snapshotFilterPanel.getSelectedItem();
if (row == null) {
currentCtx = null;
myActionContext = null;
return;
}
long snap = row.getSnap();
if (snap == current.getSnap().longValue()) {
return;
}
currentCtx = new DebuggerSnapActionContext(snap);
myActionContext = new DebuggerSnapActionContext(snap);
viewManager.activateSnap(snap);
contextChanged();
});
TableColumnModel columnModel = snapshotTable.getColumnModel();
@ -227,12 +256,29 @@ public class DebuggerTimeProvider extends ComponentProviderAdapter {
timeCol.setPreferredWidth(200);
TableColumn etCol = columnModel.getColumn(SnapshotTableColumns.EVENT_THREAD.ordinal());
etCol.setPreferredWidth(40);
TableColumn ticksCol = columnModel.getColumn(SnapshotTableColumns.TICKS.ordinal());
ticksCol.setPreferredWidth(60);
TableColumn schdCol = columnModel.getColumn(SnapshotTableColumns.SCHEDULE.ordinal());
schdCol.setPreferredWidth(60);
TableColumn descCol = columnModel.getColumn(SnapshotTableColumns.DESCRIPTION.ordinal());
descCol.setPreferredWidth(200);
}
protected void createActions() {
actionHideScratch = DebuggerResources.HideScratchSnapshotsAction.builder(plugin)
.selected(hideScratch)
.onAction(this::activatedHideScratch)
.buildAndInstallLocal(this);
}
private void activatedHideScratch(ActionContext ctx) {
hideScratch = !hideScratch;
if (hideScratch) {
deleteScratchSnapshots();
}
else {
loadScratchSnapshots();
}
}
private void addNewListeners() {
if (currentTrace == null) {
return;
@ -290,7 +336,35 @@ public class DebuggerTimeProvider extends ComponentProviderAdapter {
return;
}
TraceTimeManager manager = curTrace.getTimeManager();
snapshotTableModel.addAll(
Collections2.transform(manager.getAllSnapshots(), s -> new SnapshotRow(curTrace, s)));
Collection<? extends TraceSnapshot> snapshots = hideScratch
? manager.getSnapshots(0, true, Long.MAX_VALUE, true)
: manager.getAllSnapshots();
snapshotTableModel.addAll(Collections2.transform(snapshots,
s -> new SnapshotRow(curTrace, s)));
}
protected void deleteScratchSnapshots() {
snapshotTableModel.deleteWith(s -> s.getSnap() < 0);
}
protected void loadScratchSnapshots() {
Trace curTrace = current.getTrace();
if (curTrace == null) {
return;
}
TraceTimeManager manager = curTrace.getTimeManager();
snapshotTableModel.addAll(Collections2.transform(
manager.getSnapshots(Long.MIN_VALUE, true, 0, false),
s -> new SnapshotRow(curTrace, s)));
}
public void writeConfigState(SaveState saveState) {
CONFIG_STATE_HANDLER.writeConfigState(this, saveState);
}
public void readConfigState(SaveState saveState) {
CONFIG_STATE_HANDLER.readConfigState(this, saveState);
actionHideScratch.setSelected(hideScratch);
}
}

View file

@ -51,8 +51,8 @@ public class SnapshotRow {
return thread == null ? "" : thread.getName();
}
public long getTicks() {
return snapshot.getTicks();
public String getSchedule() {
return snapshot.getScheduleString();
}
public String getDescription() {

View file

@ -138,7 +138,7 @@ public class DebuggerWatchesProvider extends ComponentProviderAdapter {
if (!Objects.equals(a.getRecorder(), b.getRecorder())) {
return false; // May need to read target
}
if (!Objects.equals(a.getSnap(), b.getSnap())) {
if (!Objects.equals(a.getTime(), b.getTime())) {
return false;
}
if (!Objects.equals(a.getThread(), b.getThread())) {
@ -147,7 +147,6 @@ public class DebuggerWatchesProvider extends ComponentProviderAdapter {
if (!Objects.equals(a.getFrame(), b.getFrame())) {
return false;
}
// TODO: Ticks
return true;
}
@ -236,19 +235,23 @@ public class DebuggerWatchesProvider extends ComponentProviderAdapter {
@SuppressWarnings("unused")
private final AutoService.Wiring autoServiceWiring;
@AutoOptionDefined(name = DebuggerResources.OPTION_NAME_COLORS_WATCH_STALE, //
@AutoOptionDefined(
name = DebuggerResources.OPTION_NAME_COLORS_WATCH_STALE, //
description = "Text color for watches whose value is not known", //
help = @HelpInfo(anchor = "colors"))
protected Color watchStaleColor = DebuggerResources.DEFAULT_COLOR_WATCH_STALE;
@AutoOptionDefined(name = DebuggerResources.OPTION_NAME_COLORS_WATCH_STALE_SEL, //
@AutoOptionDefined(
name = DebuggerResources.OPTION_NAME_COLORS_WATCH_STALE_SEL, //
description = "Selected text color for watches whose value is not known", //
help = @HelpInfo(anchor = "colors"))
protected Color watchStaleSelColor = DebuggerResources.DEFAULT_COLOR_WATCH_STALE_SEL;
@AutoOptionDefined(name = DebuggerResources.OPTION_NAME_COLORS_WATCH_CHANGED, //
@AutoOptionDefined(
name = DebuggerResources.OPTION_NAME_COLORS_WATCH_CHANGED, //
description = "Text color for watches whose value just changed", //
help = @HelpInfo(anchor = "colors"))
protected Color watchChangesColor = DebuggerResources.DEFAULT_COLOR_WATCH_CHANGED;
@AutoOptionDefined(name = DebuggerResources.OPTION_NAME_COLORS_WATCH_CHANGED_SEL, //
@AutoOptionDefined(
name = DebuggerResources.OPTION_NAME_COLORS_WATCH_CHANGED_SEL, //
description = "Selected text color for watches whose value just changed", //
help = @HelpInfo(anchor = "colors"))
protected Color watchChangesSelColor = DebuggerResources.DEFAULT_COLOR_WATCH_CHANGED_SEL;
@ -554,7 +557,7 @@ public class DebuggerWatchesProvider extends ComponentProviderAdapter {
setRowsContext(coordinates);
if (current.isAliveAndPresent()) {
if (current.isAliveAndReadsPresent()) {
readTarget();
}
reevaluate();

View file

@ -189,10 +189,10 @@ public class WatchRow {
}
@Override
public void execute(SleighProgram program,
public PcodeFrame execute(PcodeProgram program,
SleighUseropLibrary<Pair<byte[], Address>> library) {
depsState.reset();
super.execute(program, library);
return super.execute(program, library);
}
public AddressSet getReads() {
@ -204,7 +204,7 @@ public class WatchRow {
DebuggerCoordinates coordinates) {
Trace trace = coordinates.getTrace();
ReadDepsTraceBytesPcodeExecutorState state =
new ReadDepsTraceBytesPcodeExecutorState(trace, coordinates.getSnap(),
new ReadDepsTraceBytesPcodeExecutorState(trace, coordinates.getViewSnap(),
coordinates.getThread(), coordinates.getFrame());
Language language = trace.getBaseLanguage();
PcodeExecutorState<Pair<byte[], Address>> paired =
@ -232,11 +232,11 @@ public class WatchRow {
language = (SleighLanguage) newLanguage;
recompile();
}
if (coordinates.isAliveAndPresent()) {
if (coordinates.isAliveAndReadsPresent()) {
asyncExecutor = TracePcodeUtils.executorForCoordinates(coordinates);
}
executorWithState = TraceSleighUtils.buildByteWithStateExecutor(trace,
coordinates.getSnap(), coordinates.getThread(), coordinates.getFrame());
coordinates.getViewSnap(), coordinates.getThread(), coordinates.getFrame());
executorWithAddress = buildAddressDepsExecutor(coordinates);
}

View file

@ -43,6 +43,7 @@ import ghidra.util.Msg;
import ghidra.util.task.TaskMonitor;
@DisassemblyInjectInfo(langIDs = { "x86:LE:64:default" })
// TODO: Filter / selector on debugger. This is running for GDB, too....
public class DbgengX64DisassemblyInject implements DisassemblyInject {
enum Mode {

View file

@ -0,0 +1,115 @@
/* ###
* 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.app.plugin.core.debug.service.emulation;
import java.util.concurrent.*;
import com.google.common.collect.Range;
import com.google.common.primitives.UnsignedLong;
import ghidra.app.services.TraceRecorder;
import ghidra.framework.plugintool.PluginTool;
import ghidra.pcode.exec.AccessPcodeExecutionException;
import ghidra.pcode.exec.trace.TraceCachedWriteBytesPcodeExecutorState;
import ghidra.pcode.exec.trace.TraceSleighUtils;
import ghidra.program.model.address.AddressSet;
import ghidra.program.model.address.AddressSpace;
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.util.database.UndoableTransaction;
public abstract class AbstractReadsTargetPcodeExecutorState
extends TraceCachedWriteBytesPcodeExecutorState {
abstract class AbstractReadsTargetCachedSpace extends CachedSpace {
public AbstractReadsTargetCachedSpace(AddressSpace space,
TraceMemorySpace source, long snap) {
super(space, source, snap);
}
protected abstract void fillUninitialized(AddressSet uninitialized);
protected boolean isLive() {
return recorder != null && recorder.isRecording() && recorder.getSnap() == snap;
}
protected AddressSet computeUnknown(AddressSet uninitialized) {
return uninitialized.subtract(source.getAddressesWithState(snap, uninitialized,
s -> s != null && s != TraceMemoryState.UNKNOWN));
}
@Override
public byte[] read(long offset, int size) {
if (source != null) {
AddressSet uninitialized = new AddressSet();
for (Range<UnsignedLong> rng : cache.getUninitialized(offset, offset + size)
.asRanges()) {
uninitialized.add(space.getAddress(lower(rng)),
space.getAddress(upper(rng)));
}
if (uninitialized.isEmpty()) {
return super.read(offset, size);
}
fillUninitialized(uninitialized);
}
// TODO: What to flush when bytes in the trace change?
return super.read(offset, size);
}
protected <T> T waitTimeout(CompletableFuture<T> future) {
try {
return future.get(1, TimeUnit.SECONDS);
}
catch (InterruptedException | ExecutionException | TimeoutException e) {
throw new AccessPcodeExecutionException("Timed out reading target", e);
}
}
}
protected final TraceRecorder recorder;
protected final PluginTool tool;
public AbstractReadsTargetPcodeExecutorState(PluginTool tool, Trace trace, long snap,
TraceThread thread, int frame, TraceRecorder recorder) {
super(trace, snap, thread, frame);
this.tool = tool;
this.recorder = recorder;
}
protected abstract AbstractReadsTargetCachedSpace createCachedSpace(AddressSpace s,
TraceMemorySpace tms);
@Override
protected CachedSpace getForSpace(AddressSpace space, boolean toWrite) {
return spaces.computeIfAbsent(space, s -> {
TraceMemorySpace tms;
if (s.isUniqueSpace()) {
tms = null;
}
else {
try (UndoableTransaction tid =
UndoableTransaction.start(trace, "Create space", true)) {
tms = TraceSleighUtils.getSpaceForExecution(s, trace, thread, frame, true);
}
}
return createCachedSpace(s, tms);
});
}
}

View file

@ -0,0 +1,287 @@
/* ###
* 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.app.plugin.core.debug.service.emulation;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import org.apache.commons.lang3.exception.ExceptionUtils;
import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
import ghidra.app.plugin.core.debug.event.TraceClosedPluginEvent;
import ghidra.app.services.*;
import ghidra.async.AsyncLazyMap;
import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
import ghidra.framework.plugintool.util.PluginStatus;
import ghidra.trace.model.Trace;
import ghidra.trace.model.time.TraceSchedule;
import ghidra.trace.model.time.TraceSchedule.CompareResult;
import ghidra.trace.model.time.TraceSnapshot;
import ghidra.util.database.UndoableTransaction;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.Task;
import ghidra.util.task.TaskMonitor;
@PluginInfo(
shortDescription = "Debugger Emulation Service Plugin",
description = "Manages and cache trace emulation states",
category = PluginCategoryNames.DEBUGGER,
packageName = DebuggerPluginPackage.NAME,
status = PluginStatus.UNSTABLE,
eventsConsumed = {
TraceClosedPluginEvent.class
},
servicesRequired = {
DebuggerTraceManagerService.class
},
servicesProvided = {
DebuggerEmulationService.class
})
public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEmulationService {
protected static final int MAX_CACHE_SIZE = 5;
protected static long nextSnap = Long.MIN_VALUE; // HACK
protected static class CacheKey implements Comparable<CacheKey> {
protected final Trace trace;
protected final TraceSchedule time;
public CacheKey(Trace trace, TraceSchedule time) {
this.trace = trace;
this.time = time;
}
@Override
public int hashCode() {
return Objects.hash(trace, time);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof CacheKey)) {
return false;
}
CacheKey that = (CacheKey) obj;
if (this.trace != that.trace) {
return false;
}
if (!Objects.equals(this.time, that.time)) {
return false;
}
return true;
}
@Override
public int compareTo(CacheKey that) {
return compareKey(that).compareTo;
}
public CompareResult compareKey(CacheKey that) {
CompareResult result;
// I don't care the order, I just care that traces matter first
result = CompareResult.unrelated(Integer.compare(System.identityHashCode(this.trace),
System.identityHashCode(that.trace)));
if (result != CompareResult.EQUALS) {
return result;
}
result = this.time.compareSchedule(that.time);
if (result != CompareResult.EQUALS) {
return result;
}
return CompareResult.EQUALS;
}
}
protected static class CachedEmulator {
final DebuggerTracePcodeEmulator emulator;
public CachedEmulator(DebuggerTracePcodeEmulator emulator) {
this.emulator = emulator;
}
}
protected class EmulateTask extends Task {
protected final CacheKey key;
protected final CompletableFuture<Long> future = new CompletableFuture<>();
public EmulateTask(CacheKey key) {
super("Emulate " + key.time + " in " + key.trace, true, true, false, false);
this.key = key;
}
@Override
public void run(TaskMonitor monitor) throws CancelledException {
try {
future.complete(doEmulate(key, monitor));
}
catch (CancelledException e) {
future.completeExceptionally(e);
throw e;
}
catch (Throwable e) {
future.completeExceptionally(e);
ExceptionUtils.rethrow(e);
}
}
}
protected final Set<CacheKey> eldest = new LinkedHashSet<>();
protected final NavigableMap<CacheKey, CachedEmulator> cache = new TreeMap<>();
protected final AsyncLazyMap<CacheKey, Long> requests =
new AsyncLazyMap<>(new HashMap<>(), this::doBackgroundEmulate)
.forgetErrors((key, t) -> true)
.forgetValues((key, l) -> true);
@AutoServiceConsumed
private DebuggerTraceManagerService traceManager;
@AutoServiceConsumed
private DebuggerModelService modelService;
@SuppressWarnings("unused")
private AutoService.Wiring autoServiceWiring;
public DebuggerEmulationServicePlugin(PluginTool tool) {
super(tool);
autoServiceWiring = AutoService.wireServicesProvidedAndConsumed(this);
}
protected Map.Entry<CacheKey, CachedEmulator> findNearestPrefix(CacheKey key) {
Map.Entry<CacheKey, CachedEmulator> candidate = cache.floorEntry(key);
if (candidate == null) {
return null;
}
if (!candidate.getKey().compareKey(key).related) {
return null;
}
return candidate;
}
protected CompletableFuture<Long> doBackgroundEmulate(CacheKey key) {
EmulateTask task = new EmulateTask(key);
tool.execute(task, 500);
return task.future;
}
@Override
public CompletableFuture<Long> backgroundEmulate(Trace trace, TraceSchedule time) {
if (!traceManager.getOpenTraces().contains(trace)) {
throw new IllegalArgumentException(
"Cannot emulate a trace unless it's opened in the tool.");
}
if (time.isSnapOnly()) {
return CompletableFuture.completedFuture(time.getSnap());
}
return requests.get(new CacheKey(trace, time));
}
protected TraceSnapshot findScratch(Trace trace, TraceSchedule time) {
Collection<? extends TraceSnapshot> exist =
trace.getTimeManager().getSnapshotsWithSchedule(time);
if (!exist.isEmpty()) {
return exist.iterator().next();
}
/**
* TODO: This could be more sophisticated.... Does it need to be, though? Ideally, we'd only
* keep state around that has annotations, e.g., bookmarks and code units. That needs a new
* query (latestStartSince) on those managers, though. It must find the latest start tick
* since a given snap. We consider only start snaps because placed code units go "from now
* on out".
*/
TraceSnapshot last = trace.getTimeManager().getMostRecentSnapshot(-1);
long snap = last == null ? Long.MIN_VALUE : last.getKey() + 1;
TraceSnapshot snapshot = trace.getTimeManager().getSnapshot(snap, true);
snapshot.setDescription("Emulated");
snapshot.setSchedule(time);
return snapshot;
}
protected long doEmulate(CacheKey key, TaskMonitor monitor) throws CancelledException {
Trace trace = key.trace;
TraceSchedule time = key.time;
CachedEmulator ce;
DebuggerTracePcodeEmulator emu;
Map.Entry<CacheKey, CachedEmulator> ancestor = findNearestPrefix(key);
if (ancestor != null) {
CacheKey prevKey = ancestor.getKey();
cache.remove(prevKey);
eldest.remove(prevKey);
// TODO: Handle errors, and add to proper place in cache?
// TODO: Finish partially-executed instructions?
ce = ancestor.getValue();
emu = ce.emulator;
monitor.initialize(time.totalTickCount() - prevKey.time.totalTickCount());
time.finish(trace, prevKey.time, emu, monitor);
}
else {
emu = new DebuggerTracePcodeEmulator(tool, trace, time.getSnap(),
modelService == null ? null : modelService.getRecorder(trace));
ce = new CachedEmulator(emu);
monitor.initialize(time.totalTickCount());
time.execute(trace, emu, monitor);
}
TraceSnapshot destSnap;
try (UndoableTransaction tid = UndoableTransaction.start(trace, "Emulate", true)) {
destSnap = findScratch(trace, time);
emu.writeDown(trace, destSnap.getKey(), time.getSnap(), false);
}
cache.put(key, ce);
eldest.add(key);
assert cache.size() == eldest.size();
while (cache.size() > MAX_CACHE_SIZE) {
CacheKey expired = eldest.iterator().next();
eldest.remove(expired);
cache.remove(expired);
}
return destSnap.getKey();
}
@Override
public long emulate(Trace trace, TraceSchedule time, TaskMonitor monitor)
throws CancelledException {
if (!traceManager.getOpenTraces().contains(trace)) {
throw new IllegalArgumentException(
"Cannot emulate a trace unless it's opened in the tool.");
}
if (time.isSnapOnly()) {
return time.getSnap();
}
return doEmulate(new CacheKey(trace, time), monitor);
}
@Override
public DebuggerTracePcodeEmulator getCachedEmulator(Trace trace, TraceSchedule time) {
CachedEmulator ce = cache.get(new CacheKey(trace, time));
return ce == null ? null : ce.emulator;
}
@AutoServiceConsumed
private void setTraceManager(DebuggerTraceManagerService traceManager) {
cache.clear();
}
@AutoServiceConsumed
private void setModelService(DebuggerModelService modelService) {
cache.clear();
}
}

View file

@ -0,0 +1,85 @@
/* ###
* 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.app.plugin.core.debug.service.emulation;
import ghidra.app.services.TraceRecorder;
import ghidra.framework.plugintool.PluginTool;
import ghidra.pcode.emu.BytesPcodeThread;
import ghidra.pcode.emu.PcodeThread;
import ghidra.pcode.exec.PcodeExecutorState;
import ghidra.pcode.exec.trace.TracePcodeEmulator;
import ghidra.program.model.lang.Register;
import ghidra.program.model.lang.RegisterValue;
import ghidra.trace.model.Trace;
import ghidra.trace.model.memory.TraceMemoryRegisterSpace;
import ghidra.trace.model.memory.TraceMemoryState;
import ghidra.trace.model.thread.TraceThread;
/**
* A trace emulator that knows how to read target memory when necessary
*
* <p>
* This emulator must always be run in its own thread, or at least a thread that can never lock the
* UI. It blocks on target reads so that execution can proceed synchronously. Probably the most
* suitable option is to use a background task.
*/
public class DebuggerTracePcodeEmulator extends TracePcodeEmulator {
protected final PluginTool tool;
protected final TraceRecorder recorder;
public DebuggerTracePcodeEmulator(PluginTool tool, Trace trace, long snap,
TraceRecorder recorder) {
super(trace, snap);
this.tool = tool;
this.recorder = recorder;
}
protected boolean isRegisterKnown(String threadName, Register register) {
TraceThread thread = trace.getThreadManager().getLiveThreadByPath(snap, threadName);
TraceMemoryRegisterSpace space =
trace.getMemoryManager().getMemoryRegisterSpace(thread, false);
if (space == null) {
return false;
}
return space.getState(snap, register) == TraceMemoryState.KNOWN;
}
@Override
protected BytesPcodeThread createThread(String name) {
BytesPcodeThread thread = super.createThread(name);
Register contextreg = language.getContextBaseRegister();
if (contextreg != null && !isRegisterKnown(name, contextreg)) {
RegisterValue context = trace.getRegisterContextManager()
.getValueWithDefault(language, contextreg, snap, thread.getCounter());
thread.overrideContext(context);
}
return thread;
}
@Override
protected PcodeExecutorState<byte[]> createMemoryState() {
return new ReadsTargetMemoryPcodeExecutorState(tool, trace, snap, null, 0,
recorder);
}
@Override
protected PcodeExecutorState<byte[]> createRegisterState(PcodeThread<byte[]> emuThread) {
TraceThread traceThread =
trace.getThreadManager().getLiveThreadByPath(snap, emuThread.getName());
return new ReadsTargetRegistersPcodeExecutorState(tool, trace, snap, traceThread, 0,
recorder);
}
}

View file

@ -0,0 +1,120 @@
/* ###
* 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.app.plugin.core.debug.service.emulation;
import java.util.Map.Entry;
import org.apache.commons.lang3.tuple.Pair;
import ghidra.app.services.DebuggerStaticMappingService;
import ghidra.app.services.TraceRecorder;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.trace.model.Trace;
import ghidra.trace.model.memory.TraceMemorySpace;
import ghidra.trace.model.thread.TraceThread;
import ghidra.util.MathUtilities;
import ghidra.util.Msg;
import ghidra.util.task.TaskMonitor;
public class ReadsTargetMemoryPcodeExecutorState
extends AbstractReadsTargetPcodeExecutorState {
protected class ReadsTargetMemoryCachedSpace extends AbstractReadsTargetCachedSpace {
public ReadsTargetMemoryCachedSpace(AddressSpace space, TraceMemorySpace source,
long snap) {
super(space, source, snap);
}
@Override
protected void fillUninitialized(AddressSet uninitialized) {
// TODO: fillUnknownWithStaticImages?
if (!isLive()) {
return;
}
AddressSet unknown = computeUnknown(uninitialized);
if (unknown.isEmpty()) {
return;
}
fillUnknownWithRecorder(unknown);
unknown = computeUnknown(uninitialized);
if (unknown.isEmpty()) {
return;
}
Msg.warn(this, "Emulator read from UNKNOWN state: " + unknown);
}
protected void fillUnknownWithRecorder(AddressSet unknown) {
waitTimeout(recorder.captureProcessMemory(unknown, TaskMonitor.DUMMY));
}
private void fillUnknownWithStaticImages(AddressSet unknown) {
if (!space.isMemorySpace()) {
return;
}
DebuggerStaticMappingService mappingService =
tool.getService(DebuggerStaticMappingService.class);
byte[] data = new byte[4096];
for (Entry<Program, Pair<Long, AddressSetView>> ent : mappingService
.getOpenMappedViews(trace, unknown, snap)
.entrySet()) {
Program program = ent.getKey();
Pair<Long, AddressSetView> pair = ent.getValue();
Msg.warn(this,
"Filling in unknown trace memory in emulator using mapped image: " +
program + ": " + pair.getRight());
long shift = pair.getLeft();
Memory memory = program.getMemory();
for (AddressRange rng : pair.getRight()) {
long lower = rng.getMinAddress().getOffset();
long fullLen = rng.getLength();
while (fullLen > 0) {
int len = MathUtilities.unsignedMin(data.length, fullLen);
try {
int read =
memory.getBytes(space.getAddress(lower), data, 0, len);
if (read < len) {
Msg.warn(this,
" Partial read of " + rng + ". Got " + read + " bytes");
}
cache.putData(lower - shift, data, 0, read);
}
catch (MemoryAccessException | AddressOutOfBoundsException e) {
throw new AssertionError(e);
}
lower += len;
fullLen -= len;
}
}
}
}
}
public ReadsTargetMemoryPcodeExecutorState(PluginTool tool, Trace trace, long snap,
TraceThread thread, int frame, TraceRecorder recorder) {
super(tool, trace, snap, thread, frame, recorder);
}
@Override
protected AbstractReadsTargetCachedSpace createCachedSpace(AddressSpace s,
TraceMemorySpace tms) {
return new ReadsTargetMemoryCachedSpace(s, tms, snap);
}
}

View file

@ -0,0 +1,76 @@
/* ###
* 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.app.plugin.core.debug.service.emulation;
import java.util.HashSet;
import java.util.Set;
import ghidra.app.services.TraceRecorder;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.Register;
import ghidra.trace.model.Trace;
import ghidra.trace.model.memory.TraceMemorySpace;
import ghidra.trace.model.thread.TraceThread;
import ghidra.util.Msg;
public class ReadsTargetRegistersPcodeExecutorState
extends AbstractReadsTargetPcodeExecutorState {
protected class ReadsTargetRegistersCachedSpace extends AbstractReadsTargetCachedSpace {
public ReadsTargetRegistersCachedSpace(AddressSpace space, TraceMemorySpace source,
long snap) {
super(space, source, snap);
}
@Override
protected void fillUninitialized(AddressSet uninitialized) {
if (!isLive()) {
return;
}
AddressSet unknown = computeUnknown(uninitialized);
Set<Register> toRead = new HashSet<>();
for (AddressRange rng : unknown) {
Register register =
language.getRegister(rng.getMinAddress(), (int) rng.getLength());
if (register == null) {
Msg.error(this, "Could not figure register for " + rng);
}
else if (!recorder.getRegisterMapper(thread)
.getRegistersOnTarget()
.contains(register)) {
Msg.warn(this, "Register not recognized by target: " + register);
}
else {
toRead.add(register);
}
}
waitTimeout(recorder.captureThreadRegisters(thread, 0, toRead));
}
}
public ReadsTargetRegistersPcodeExecutorState(PluginTool tool, Trace trace, long snap,
TraceThread thread, int frame, TraceRecorder recorder) {
super(tool, trace, snap, thread, frame, recorder);
}
@Override
protected AbstractReadsTargetCachedSpace createCachedSpace(AddressSpace s,
TraceMemorySpace tms) {
return new ReadsTargetRegistersCachedSpace(s, tms, snap);
}
}

View file

@ -23,6 +23,8 @@ import java.util.Map.Entry;
import java.util.stream.Collectors;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import com.google.common.collect.Range;
@ -284,6 +286,7 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
private Program program;
private AddressRange staticRange;
private Long shift;
public MappingEntry(TraceStaticMapping mapping) {
this.mapping = mapping;
@ -309,6 +312,7 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
Address minAddr = opened.getAddressFactory().getAddress(mapping.getStaticAddress());
Address maxAddr = addOrMax(minAddr, mapping.getLength() - 1);
this.staticRange = new AddressRangeImpl(minAddr, maxAddr);
this.shift = mapping.getMinTraceAddress().subtract(staticRange.getMinAddress());
return true;
}
return false;
@ -318,6 +322,7 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
if (this.program == closed) {
this.program = null;
this.staticRange = null;
this.shift = null;
return true;
}
return false;
@ -562,7 +567,7 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
}
protected void collectOpenMappedPrograms(AddressRange rng, Range<Long> span,
Map<Program, AddressSet> result) {
Map<Program, Pair<Long, AddressSetView>> result) {
TraceAddressSnapRange tatr = new ImmutableTraceAddressSnapRange(rng, span);
for (Entry<TraceAddressSnapRange, MappingEntry> out : outbound.entrySet()) {
MappingEntry me = out.getValue();
@ -572,14 +577,16 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
if (!out.getKey().intersects(tatr)) {
continue;
}
AddressSet set = result.computeIfAbsent(me.program, p -> new AddressSet());
set.add(me.mapTraceRangeToProgram(rng));
Pair<Long, AddressSetView> set = result.computeIfAbsent(me.program,
p -> new ImmutablePair<>(me.shift, new AddressSet()));
((AddressSet) set.getRight()).add(me.mapTraceRangeToProgram(rng));
}
}
public Map<Program, AddressSetView> getOpenMappedViews(AddressSetView set,
public Map<Program, Pair<Long, AddressSetView>> getOpenMappedViews(AddressSetView set,
Range<Long> span) {
Map<Program, AddressSet> result = new HashMap<>();
Map<Program, Pair<Long, AddressSetView>> result = new HashMap<>();
for (AddressRange rng : set) {
collectOpenMappedPrograms(rng, span, result);
}
@ -709,7 +716,8 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
return null;
}
protected void collectOpenMappedViews(AddressRange rng, Map<TraceSnap, AddressSet> result) {
protected void collectOpenMappedViews(AddressRange rng,
Map<TraceSnap, Pair<Long, AddressSetView>> result) {
for (Entry<MappingEntry, Address> inPreceeding : inbound.headMapByValue(
rng.getMaxAddress(), true).entrySet()) {
Address start = inPreceeding.getValue();
@ -720,13 +728,14 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
if (!me.isInProgramRange(rng)) {
continue;
}
AddressSet set = result.computeIfAbsent(me.getTraceSnap(), p -> new AddressSet());
set.add(me.mapProgramRangeToTrace(rng));
Pair<Long, AddressSetView> set = result.computeIfAbsent(me.getTraceSnap(),
p -> new ImmutablePair<>(me.shift, new AddressSet()));
((AddressSet) set.getRight()).add(me.mapProgramRangeToTrace(rng));
}
}
public Map<TraceSnap, AddressSetView> getOpenMappedViews(AddressSetView set) {
Map<TraceSnap, AddressSet> result = new HashMap<>();
public Map<TraceSnap, Pair<Long, AddressSetView>> getOpenMappedViews(AddressSetView set) {
Map<TraceSnap, Pair<Long, AddressSetView>> result = new HashMap<>();
for (AddressRange rng : set) {
collectOpenMappedViews(rng, result);
}
@ -1099,13 +1108,17 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
return info.getOpenMappedLocations(loc.getAddress(), loc.getLifespan());
}
protected long getNonScratchSnap(TraceProgramView view) {
return view.getViewport().getTop(s -> s >= 0 ? s : null);
}
@Override
public ProgramLocation getStaticLocationFromDynamic(ProgramLocation loc) {
loc = traceManager.fixLocation(loc, true);
TraceProgramView view = (TraceProgramView) loc.getProgram();
Trace trace = view.getTrace();
TraceLocation tloc = new DefaultTraceLocation(trace, null, Range.singleton(view.getSnap()),
loc.getAddress());
TraceLocation tloc = new DefaultTraceLocation(trace, null,
Range.singleton(getNonScratchSnap(view)), loc.getAddress());
ProgramLocation mapped = getOpenMappedLocation(tloc);
if (mapped == null) {
return null;
@ -1134,7 +1147,7 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
@Override
public ProgramLocation getDynamicLocationFromStatic(TraceProgramView view,
ProgramLocation loc) {
TraceLocation tloc = getOpenMappedLocation(view.getTrace(), loc, view.getSnap());
TraceLocation tloc = getOpenMappedLocation(view.getTrace(), loc, getNonScratchSnap(view));
if (tloc == null) {
return null;
}
@ -1142,7 +1155,8 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
}
@Override
public Map<Program, AddressSetView> getOpenMappedViews(Trace trace, AddressSetView set,
public Map<Program, Pair<Long, AddressSetView>> getOpenMappedViews(Trace trace,
AddressSetView set,
long snap) {
InfoPerTrace info = requireTrackedInfo(trace);
if (info == null) {
@ -1152,7 +1166,8 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
}
@Override
public Map<TraceSnap, AddressSetView> getOpenMappedViews(Program program, AddressSetView set) {
public Map<TraceSnap, Pair<Long, AddressSetView>> getOpenMappedViews(Program program,
AddressSetView set) {
InfoPerProgram info = requireTrackedInfo(program);
if (info == null) {
return null;

View file

@ -56,6 +56,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.util.*;
import ghidra.util.datastruct.CollectionChangeListener;
import ghidra.util.exception.*;
@ -119,8 +121,9 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
}
private void threadDeleted(TraceThread thread) {
if (threadFocusByTrace.get(trace) == thread) {
threadFocusByTrace.remove(trace);
DebuggerCoordinates last = lastCoordsByTrace.get(trace);
if (last != null && last.getThread() == thread) {
lastCoordsByTrace.remove(trace);
}
if (current.getThread() == thread) {
activate(DebuggerCoordinates.trace(trace));
@ -158,7 +161,7 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
}
}
protected final Map<Trace, TraceThread> threadFocusByTrace = new WeakHashMap<>();
protected final Map<Trace, DebuggerCoordinates> lastCoordsByTrace = new WeakHashMap<>();
protected final Map<Trace, ListenerForTraceChanges> listenersByTrace = new WeakHashMap<>();
protected final Set<Trace> tracesView = Collections.unmodifiableSet(listenersByTrace.keySet());
@ -177,6 +180,8 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
// @AutoServiceConsumed via method
private DebuggerModelService modelService;
@AutoServiceConsumed
private DebuggerEmulationService emulationService;
@SuppressWarnings("unused")
private final AutoService.Wiring autoServiceWiring;
@ -422,6 +427,7 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
if (trace == null) {
return DebuggerCoordinates.NOWHERE;
}
DebuggerCoordinates lastForTrace = lastCoordsByTrace.get(trace);
// Note: override recorder with that known to service
TraceRecorder recorder = computeRecorder(trace);
TargetObject focus = recorder == null ? null : recorder.getFocus();
@ -431,7 +437,7 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
thread = threadFromTargetFocus(recorder, focus);
}
if (thread /*still*/ == null) { // either no focus support, or focus is not a thread
thread = threadFocusByTrace.get(trace);
thread = lastForTrace == null ? null : lastForTrace.getThread();
}
// NOTE, if still null without focus support,
// we will take the eldest live thread at the resolved snap
@ -444,20 +450,20 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
*/
// Note: override view. May not agree on snap now, but will upon activation
TraceProgramView view = trace.getProgramView();
Long snap = coordinates.getSnap();
if (snap == null) {
TraceSchedule time = coordinates.getTime();
if (time == null) {
if (recorder != null && autoActivatePresent.get() && trace != current.getTrace()) {
snap = recorder.getSnap();
time = TraceSchedule.snap(recorder.getSnap());
}
else {
snap = view.getSnap();
time = lastForTrace == null ? TraceSchedule.snap(0) : lastForTrace.getTime();
}
}
if (!supportsFocus(recorder)) {
if (thread /*still*/ == null) {
Iterator<? extends TraceThread> it =
trace.getThreadManager().getLiveThreads(snap).iterator();
trace.getThreadManager().getLiveThreads(time.getSnap()).iterator();
// docs say eldest come first
if (it.hasNext()) {
thread = it.next();
@ -472,15 +478,6 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
}
}
String ticks = coordinates.getTicks();
// TODO: Memorize ticks by trace and frame by thread
if (ticks == null && trace == current.getTrace()) {
ticks = current.getTicks();
}
if (ticks == null) {
ticks = "";
}
// Note, not likely we can view non-zero frame with emulated ticks
Integer frame = coordinates.getFrame();
if (frame == null) {
if (supportsFocus(recorder)) {
@ -493,17 +490,20 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
frame = traceFrame.getLevel();
}
}
if (frame /*still*/ == null && thread == current.getThread()) {
frame = current.getFrame();
if (frame /*still*/ == null && lastForTrace != null &&
thread == lastForTrace.getThread()) {
// TODO: Memorize frame by thread, instead of by trace?
frame = lastForTrace.getFrame();
}
}
// TODO: Is it reasonable to change back to frame 0 on snap change?
// Only 0 (possibly synthetic) is guaranteed to exist in any snap
if (frame == null || !"".equals(ticks) || !Objects.equals(snap, current.getSnap())) {
if (frame == null || !time.isSnapOnly() ||
!Objects.equals(time.getSnap(), current.getSnap())) {
frame = 0;
}
return DebuggerCoordinates.all(trace, recorder, thread, view, Objects.requireNonNull(snap),
Objects.requireNonNull(ticks), Objects.requireNonNull(frame));
return DebuggerCoordinates.all(trace, recorder, thread, view, Objects.requireNonNull(time),
Objects.requireNonNull(frame));
}
protected DebuggerCoordinates doSetCurrent(DebuggerCoordinates newCurrent) {
@ -515,8 +515,8 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
}
current = resolved;
contextChanged();
if (current.getTrace() != null && current.getThread() != null) {
threadFocusByTrace.put(current.getTrace(), current.getThread());
if (resolved.getTrace() != null) {
lastCoordsByTrace.put(resolved.getTrace(), resolved);
}
return resolved;
}
@ -561,10 +561,10 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
}
TraceThread thread = threadFromTargetFocus(recorder, obj);
long snap = recorder.getSnap();
String ticks = "";
TraceStackFrame traceFrame = frameFromTargetFocus(recorder, obj);
Integer frame = traceFrame == null ? null : traceFrame.getLevel();
activateNoFocus(DebuggerCoordinates.all(trace, recorder, thread, null, snap, ticks, frame));
activateNoFocus(DebuggerCoordinates.all(trace, recorder, thread, null,
TraceSchedule.snap(snap), frame));
return true;
}
@ -651,7 +651,8 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
@Override
public TraceThread getCurrentThreadFor(Trace trace) {
return threadFocusByTrace.get(trace);
DebuggerCoordinates coords = lastCoordsByTrace.get(trace);
return coords == null ? null : coords.getThread();
}
@Override
@ -664,14 +665,44 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
return current.getFrame();
}
protected void fireLocationEvent(DebuggerCoordinates coordinates) {
protected void prepareViewAndFireEvent(DebuggerCoordinates coordinates) {
TraceVariableSnapProgramView varView = (TraceVariableSnapProgramView) coordinates.getView();
if (varView != null) {
varView.setSnap(coordinates.getSnap());
if (varView == null) { // Should only happen with NOWHERE
fireLocationEvent(coordinates);
}
Swing.runIfSwingOrRunLater(() -> {
else if (coordinates.getTime().isSnapOnly()) {
varView.setSnap(coordinates.getSnap());
fireLocationEvent(coordinates);
}
else {
Collection<? extends TraceSnapshot> suitable = coordinates.getTrace()
.getTimeManager()
.getSnapshotsWithSchedule(coordinates.getTime());
if (!suitable.isEmpty()) {
TraceSnapshot found = suitable.iterator().next();
varView.setSnap(found.getKey());
fireLocationEvent(coordinates);
return;
}
if (emulationService == null) {
throw new IllegalStateException(
"Cannot navigate to coordinates with execution schedules, " +
"because the emulation service is not available.");
}
CompletableFuture<Long> bg =
emulationService.backgroundEmulate(coordinates.getTrace(), coordinates.getTime());
bg.thenAccept(emuSnap -> Swing.runLater(() -> {
if (!coordinates.equals(current)) {
return; // We navigated elsewhere before emulation completed
}
varView.setSnap(emuSnap);
fireLocationEvent(coordinates);
}));
}
}
protected void fireLocationEvent(DebuggerCoordinates coordinates) {
firePluginEvent(new TraceActivatedPluginEvent(getName(), coordinates));
});
}
@Override
@ -874,7 +905,7 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
protected void doTraceClosed(Trace trace) {
synchronized (listenersByTrace) {
trace.release(this);
threadFocusByTrace.remove(trace);
lastCoordsByTrace.remove(trace);
trace.removeListener(listenersByTrace.remove(trace));
//Msg.debug(this, "Remaining Consumers of " + trace + ": " + trace.getConsumerList());
}
@ -909,12 +940,12 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
while (it.hasNext()) {
Trace trace = it.next();
trace.release(this);
threadFocusByTrace.remove(trace);
lastCoordsByTrace.remove(trace);
trace.removeListener(listenersByTrace.get(trace));
it.remove();
}
// Be certain
threadFocusByTrace.clear();
lastCoordsByTrace.clear();
}
autoServiceWiring.dispose();
}
@ -931,7 +962,7 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
if (resolved == null) {
return;
}
fireLocationEvent(resolved);
prepareViewAndFireEvent(resolved);
}
protected static TargetObject translateToFocus(DebuggerCoordinates prev,
@ -970,7 +1001,7 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
if (resolved == null) {
return;
}
fireLocationEvent(resolved);
prepareViewAndFireEvent(resolved);
if (!synchronizeFocus.get()) {
return;
}
@ -997,6 +1028,11 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
activate(DebuggerCoordinates.snap(snap));
}
@Override
public void activateTime(TraceSchedule time) {
activate(DebuggerCoordinates.time(time));
}
@Override
public void activateFrame(int frameLevel) {
activate(DebuggerCoordinates.frame(frameLevel));
@ -1142,24 +1178,20 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
}
List<Trace> traces;
DebuggerCoordinates currentCoords;
Map<Trace, TraceThread> threadByTrace;
Map<Trace, Long> snapByTrace;
Map<Trace, DebuggerCoordinates> coordsByTrace;
synchronized (listenersByTrace) {
currentCoords = current;
traces = tracesView.stream().filter(t -> {
ProjectLocator loc = t.getDomainFile().getProjectLocator();
return loc != null && !loc.isTransient();
}).collect(Collectors.toList());
threadByTrace = Map.copyOf(threadFocusByTrace);
snapByTrace = tracesView.stream()
.collect(Collectors.toMap(t -> t, t -> t.getProgramView().getSnap()));
coordsByTrace = Map.copyOf(lastCoordsByTrace);
}
saveState.putInt(KEY_TRACE_COUNT, traces.size());
for (int index = 0; index < traces.size(); index++) {
Trace t = traces.get(index);
DebuggerCoordinates coords = DebuggerCoordinates.all(t, null, threadByTrace.get(t),
null, snapByTrace.get(t), null, null);
DebuggerCoordinates coords = coordsByTrace.get(t);
String stateName = PREFIX_OPEN_TRACE + index;
coords.writeDataState(tool, saveState, stateName);
}
@ -1173,7 +1205,11 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
for (int index = 0; index < traceCount; index++) {
String stateName = PREFIX_OPEN_TRACE + index;
// Trace will be opened by readDataState, resolve causes update to focus and view
DebuggerCoordinates coords =
DebuggerCoordinates.readDataState(tool, saveState, stateName, true);
if (coords.getTrace() != null) {
lastCoordsByTrace.put(coords.getTrace(), coords);
}
}
activate(DebuggerCoordinates.readDataState(tool, saveState, KEY_CURRENT_COORDS, false));

View file

@ -44,10 +44,8 @@ import ghidra.trace.model.memory.*;
import ghidra.trace.model.program.TraceProgramView;
import ghidra.trace.model.stack.*;
import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.util.TraceAddressSpace;
import ghidra.trace.util.TraceRegisterUtils;
import ghidra.util.IntersectionAddressSetView;
import ghidra.util.UnionAddressSetView;
import ghidra.trace.util.*;
import ghidra.util.*;
import ghidra.util.classfinder.ClassSearcher;
import ghidra.util.database.UndoableTransaction;
import ghidra.util.task.TaskMonitor;
@ -64,12 +62,11 @@ public class DisassembleAtPcDebuggerBot implements DebuggerBot {
private final TraceStackManager stackManager;
private final TraceMemoryManager memoryManager;
private final TraceCodeManager codeManager;
private final TraceTimeViewport viewport;
private final Register pc;
private final AddressRange pcRange;
private boolean usesStacks = false;
private final Set<DisassemblyInject> injects = new LinkedHashSet<>();
private final ChangeListener injectsChangeListener = e -> updateInjects();
@ -83,6 +80,7 @@ public class DisassembleAtPcDebuggerBot implements DebuggerBot {
this.stackManager = trace.getStackManager();
this.memoryManager = trace.getMemoryManager();
this.codeManager = trace.getCodeManager();
this.viewport = trace.getProgramView().getViewport();
this.pc = trace.getBaseLanguage().getProgramCounter();
this.pcRange = TraceRegisterUtils.rangeForRegister(pc);
@ -117,6 +115,7 @@ public class DisassembleAtPcDebuggerBot implements DebuggerBot {
}
private void processQueue(Void __) {
try {
List<Runnable> copy;
synchronized (runQueue) {
copy = List.copyOf(runQueue);
@ -126,6 +125,10 @@ public class DisassembleAtPcDebuggerBot implements DebuggerBot {
r.run();
}
}
catch (Throwable e) {
Msg.error(this, "Error processing queue", e);
}
}
private void valuesChanged(TraceAddressSpace space, TraceAddressSnapRange range,
byte[] oldValue, byte[] newValue) {
@ -139,21 +142,41 @@ public class DisassembleAtPcDebuggerBot implements DebuggerBot {
private void stackChanged(TraceStack stack) {
queueRunnable(() -> {
usesStacks = true;
disassembleStackPcVals(stack, stack.getSnap(), null);
});
}
private void memoryChanged(TraceAddressSnapRange range) {
queueRunnable(() -> {
long snap = range.getY1();
for (TraceThread thread : trace.getThreadManager().getLiveThreads(snap)) {
TraceStack stack = stackManager.getLatestStack(thread, snap);
if (stack != null) {
usesStacks = true;
disassembleStackPcVals(stack, snap, range.getRange());
private long findNonScratchSnap(long snap) {
if (snap >= 0) {
return snap;
}
TraceViewportSpanIterator spit = new TraceViewportSpanIterator(trace, snap);
while (spit.hasNext()) {
Range<Long> span = spit.next();
if (span.upperEndpoint() >= 0) {
return span.upperEndpoint();
}
}
return snap;
}
private void memoryChanged(TraceAddressSnapRange range) {
if (!viewport.containsAnyUpper(range.getLifespan())) {
return;
}
// This is a wonky case, because we care about where the user is looking.
long pcSnap = trace.getProgramView().getSnap();
long memSnap = range.getY1();
queueRunnable(() -> {
for (TraceThread thread : trace.getThreadManager()
.getLiveThreads(findNonScratchSnap(pcSnap))) {
TraceStack stack = stackManager.getLatestStack(thread, pcSnap);
if (stack != null) {
disassembleStackPcVals(stack, memSnap, range.getRange());
}
else {
disassembleRegPcVal(thread, 0, pcSnap, memSnap);
}
disassembleRegPcVal(thread, 0, snap);
}
});
}
@ -166,11 +189,16 @@ public class DisassembleAtPcDebuggerBot implements DebuggerBot {
if (!range.getRange().intersects(pcRange)) {
return;
}
disassembleRegPcVal(space.getThread(), space.getFrameLevel(), range.getY1());
TraceThread thread = space.getThread();
long snap = range.getY1();
if (stackManager.getLatestStack(thread, snap) != null) {
return;
}
disassembleRegPcVal(thread, space.getFrameLevel(), snap, snap);
});
}
protected void disassembleStackPcVals(TraceStack stack, long snap, AddressRange range) {
protected void disassembleStackPcVals(TraceStack stack, long memSnap, AddressRange range) {
TraceStackFrame frame = stack.getFrame(0, false);
if (frame == null) {
return;
@ -181,11 +209,12 @@ public class DisassembleAtPcDebuggerBot implements DebuggerBot {
}
if (range == null || range.contains(pcVal)) {
// NOTE: If non-0 frames are ever used, level should be passed in for injects
disassemble(pcVal, stack.getThread(), snap);
disassemble(pcVal, stack.getThread(), memSnap);
}
}
protected void disassembleRegPcVal(TraceThread thread, int frameLevel, long snap) {
protected void disassembleRegPcVal(TraceThread thread, int frameLevel, long pcSnap,
long memSnap) {
TraceData pcUnit = null;
try (UndoableTransaction tid =
UndoableTransaction.start(trace, "Disassemble: PC is code pointer", true)) {
@ -193,74 +222,80 @@ public class DisassembleAtPcDebuggerBot implements DebuggerBot {
codeManager.getCodeRegisterSpace(thread, frameLevel, true);
try {
pcUnit = regCode.definedData()
.create(Range.atLeast(snap), pc, PointerDataType.dataType);
.create(Range.atLeast(pcSnap), pc, PointerDataType.dataType);
}
catch (CodeUnitInsertionException e) {
// I guess something's already there. Leave it, then!
// Try to get it, in case it's already a pointer type
pcUnit = regCode.definedData().getForRegister(snap, pc);
pcUnit = regCode.definedData().getForRegister(pcSnap, pc);
}
}
if (!usesStacks && pcUnit != null) {
if (pcUnit != null) {
Address pcVal = (Address) TraceRegisterUtils.getValueHackPointer(pcUnit);
if (pcVal != null) {
disassemble(pcVal, thread, snap);
disassemble(pcVal, thread, memSnap);
}
}
}
protected boolean isKnownRWOrEverKnownRO(Address start, long snap) {
Entry<TraceAddressSnapRange, TraceMemoryState> ent =
memoryManager.getMostRecentStateEntry(snap, start);
if (ent == null || ent.getValue() != TraceMemoryState.KNOWN) {
protected Long isKnownRWOrEverKnownRO(Address start, long snap) {
Entry<Long, TraceMemoryState> kent = memoryManager.getViewState(snap, start);
if (kent != null && kent.getValue() == TraceMemoryState.KNOWN) {
return kent.getKey();
}
Entry<TraceAddressSnapRange, TraceMemoryState> mrent =
memoryManager.getViewMostRecentStateEntry(snap, start);
if (mrent == null || mrent.getValue() != TraceMemoryState.KNOWN) {
// It has never been known up to this snap
return false;
return null;
}
if (ent.getKey().getLifespan().contains(snap)) {
// It is known at this snap, so RO vs RW is irrelevant
return true;
}
TraceMemoryRegion region = memoryManager.getRegionContaining(snap, start);
if (region.isWrite()) {
TraceMemoryRegion region =
memoryManager.getRegionContaining(mrent.getKey().getY1(), start);
if (region == null || region.isWrite()) {
// It could have changed this snap, so unknown
return false;
return null;
}
return true;
return mrent.getKey().getY1();
}
protected void disassemble(Address start, TraceThread thread, long snap) {
if (!isKnownRWOrEverKnownRO(start, snap)) {
Long knownSnap = isKnownRWOrEverKnownRO(start, snap);
if (knownSnap == null) {
return;
}
if (codeManager.definedUnits().containsAddress(snap, start)) {
long ks = knownSnap;
if (codeManager.definedUnits().containsAddress(ks, start)) {
return;
}
/**
* TODO: Is this composition of laziness upon laziness efficient enough?
*
* <p>
* Can experiment with ordering of address-set-view "expression" to optimize early
* termination.
*
* <p>
* Want addresses satisfying {@code known | (readOnly & everKnown)}
*/
AddressSetView readOnly =
memoryManager.getRegionsAddressSetWith(snap, r -> !r.isWrite());
AddressSetView everKnown = memoryManager.getAddressesWithState(Range.atMost(snap),
memoryManager.getRegionsAddressSetWith(ks, r -> !r.isWrite());
AddressSetView everKnown = memoryManager.getAddressesWithState(Range.atMost(ks),
s -> s == TraceMemoryState.KNOWN);
AddressSetView roEverKnown = new IntersectionAddressSetView(readOnly, everKnown);
AddressSetView known =
memoryManager.getAddressesWithState(snap, s -> s == TraceMemoryState.KNOWN);
AddressSetView disassemblable = new UnionAddressSetView(known, roEverKnown);
memoryManager.getAddressesWithState(ks, s -> s == TraceMemoryState.KNOWN);
AddressSetView disassemblable =
new AddressSet(new UnionAddressSetView(known, roEverKnown));
// TODO: Should I just keep a variable-snap view around?
TraceProgramView view = trace.getFixedProgramView(snap);
TraceProgramView view = trace.getFixedProgramView(ks);
DisassembleCommand dis =
new DisassembleCommand(start, disassemblable, true) {
@Override
public boolean applyTo(DomainObject obj, TaskMonitor monitor) {
synchronized (injects) {
if (codeManager.definedUnits().containsAddress(snap, start)) {
if (codeManager.definedUnits().containsAddress(ks, start)) {
return true;
}
for (DisassemblyInject i : injects) {

View file

@ -0,0 +1,83 @@
/* ###
* 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.app.services;
import java.util.concurrent.CompletableFuture;
import ghidra.app.plugin.core.debug.service.emulation.DebuggerEmulationServicePlugin;
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.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
@ServiceInfo(defaultProvider = DebuggerEmulationServicePlugin.class)
public interface DebuggerEmulationService {
/**
* Perform emulation to realize the machine state of the given time coordinates
*
* <p>
* Only those address ranges actually modified during emulation are written into the scratch
* space. It is the responsibility of anyone reading from scratch space to retrieve state and/or
* annotations from the initial snap, when needed. The scratch snapshot is given the description
* "{@code emu:[time]}", where {@code [time]} is the given time parameter as a string.
*
* <p>
* The service may use a cached emulator in order to realize the requested machine state. This
* is especially important to ensure that a user stepping forward does not incur ever increasing
* costs. On the other hand, the service should be careful to invalidate cached results when the
* recorded machine state in a trace changes.
*
* @param trace the trace containing the initial state
* @param time the time coordinates, including initial snap, steps, and p-code steps
* @param monitor a monitor for cancellation and progress reporting
* @return the snap in the trace's scratch space where the realized state is stored
* @throws CancelledException if the emulation is cancelled
*/
long emulate(Trace trace, TraceSchedule time, TaskMonitor monitor) throws CancelledException;
/**
* Invoke {@link #emulate(Trace, TraceSchedule, TaskMonitor)} in the background
*
* <p>
* This is the preferred means of performing emulation. Because the underlying emulator may
* request <em>blocking</em> read of a target, it is important that
* {@link #emulate(Trace, TraceSchedule, TaskMonitor)} is <em>never</em> called by the Swing
* thread.
*
* @param trace the trace containing the initial state
* @param time the time coordinates, including initial snap, steps, and p-code steps
* @return a future which completes with the result of
* {@link #emulate(Trace, TraceSchedule, TaskMonitor)}
*/
CompletableFuture<Long> backgroundEmulate(Trace trace, TraceSchedule time);
/**
* The the cached emulator for the given trace and time
*
* <p>
* To guarantee the emulator is present, call {@link #backgroundEmulate(Trace, TraceSchedule)}
* first. WARNING: This emulator belongs to this service. Stepping it, or otherwise manipulating
* it without the service's knowledge can lead to unintended consequences.
*
* @param trace the trace containing the initial state
* @param time the time coordinates, including initial snap, steps, and p-code steps
* @return the copied p-code frame
*/
DebuggerTracePcodeEmulator getCachedEmulator(Trace trace, TraceSchedule time);
}

View file

@ -18,6 +18,8 @@ package ghidra.app.services;
import java.util.*;
import java.util.stream.Collectors;
import org.apache.commons.lang3.tuple.Pair;
import com.google.common.collect.Range;
import ghidra.framework.model.DomainFile;
@ -637,7 +639,8 @@ public interface DebuggerStaticMappingService {
* @param snap the source snap
* @return a map of destination programs to corresponding computed destination address sets
*/
Map<Program, AddressSetView> getOpenMappedViews(Trace trace, AddressSetView set, long snap);
Map<Program, Pair<Long, AddressSetView>> getOpenMappedViews(Trace trace,
AddressSetView set, long snap);
/**
* Find/compute all source address sets given a destination program address set
@ -646,7 +649,8 @@ public interface DebuggerStaticMappingService {
* @param set the destination address set, from which we are mapping back
* @return a map of source traces to corresponding computed source address sets
*/
Map<TraceSnap, AddressSetView> getOpenMappedViews(Program program, AddressSetView set);
Map<TraceSnap, Pair<Long, AddressSetView>> getOpenMappedViews(Program program,
AddressSetView set);
/**
* Open all destination programs in mappings intersecting the given source trace, address set,

View file

@ -26,6 +26,7 @@ import ghidra.program.util.ProgramLocation;
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.util.TriConsumer;
@ServiceInfo(defaultProvider = DebuggerTraceManagerServicePlugin.class)
@ -128,6 +129,8 @@ public interface DebuggerTraceManagerService {
void activateSnap(long snap);
void activateTime(TraceSchedule time);
void activateFrame(int frameLevel);
void setAutoActivatePresent(boolean enabled);

View file

@ -45,17 +45,27 @@ public class AsyncPcodeExecutor<T> extends PcodeExecutor<CompletableFuture<T>> {
}
public CompletableFuture<Void> stepOpAsync(PcodeOp op, PcodeFrame frame,
Map<Integer, String> useropNames, SleighUseropLibrary<CompletableFuture<T>> library) {
SleighUseropLibrary<CompletableFuture<T>> library) {
if (op.getOpcode() == PcodeOp.CBRANCH) {
return executeConditionalBranchAsync(op, frame);
}
stepOp(op, frame, useropNames, library);
stepOp(op, frame, library);
return AsyncUtils.NIL;
}
public CompletableFuture<Void> stepAsync(PcodeFrame frame, Map<Integer, String> useropNames,
public CompletableFuture<Void> stepAsync(PcodeFrame frame,
SleighUseropLibrary<CompletableFuture<T>> library) {
return stepOpAsync(frame.nextOp(), frame, useropNames, library);
try {
return stepOpAsync(frame.nextOp(), frame, library);
}
catch (PcodeExecutionException e) {
e.frame = frame;
return CompletableFuture.failedFuture(e);
}
catch (Exception e) {
return CompletableFuture.failedFuture(
new PcodeExecutionException("Exception during pcode execution", frame, e));
}
}
public CompletableFuture<Void> executeConditionalBranchAsync(PcodeOp op, PcodeFrame frame) {
@ -68,23 +78,23 @@ public class AsyncPcodeExecutor<T> extends PcodeExecutor<CompletableFuture<T>> {
});
}
public CompletableFuture<Void> executeAsync(SleighProgram program,
public CompletableFuture<Void> executeAsync(PcodeProgram program,
SleighUseropLibrary<CompletableFuture<T>> library) {
return executeAsync(program.code, program.useropNames, library);
}
protected CompletableFuture<Void> executeAsyncLoop(PcodeFrame frame,
Map<Integer, String> useropNames, SleighUseropLibrary<CompletableFuture<T>> library) {
SleighUseropLibrary<CompletableFuture<T>> library) {
if (frame.isFinished()) {
return AsyncUtils.NIL;
}
return stepAsync(frame, useropNames, library)
.thenComposeAsync(__ -> executeAsyncLoop(frame, useropNames, library));
return stepAsync(frame, library)
.thenComposeAsync(__ -> executeAsyncLoop(frame, library));
}
public CompletableFuture<Void> executeAsync(List<PcodeOp> code,
Map<Integer, String> useropNames, SleighUseropLibrary<CompletableFuture<T>> library) {
PcodeFrame frame = new PcodeFrame(code);
return executeAsyncLoop(frame, useropNames, library);
PcodeFrame frame = new PcodeFrame(language, code, useropNames);
return executeAsyncLoop(frame, library);
}
}

View file

@ -61,6 +61,11 @@ public class AsyncWrappedPcodeArithmetic<T> implements PcodeArithmetic<Completab
return CompletableFuture.completedFuture(arithmetic.fromConst(value, size));
}
@Override
public CompletableFuture<T> fromConst(BigInteger value, int size) {
return CompletableFuture.completedFuture(arithmetic.fromConst(value, size));
}
@Override
public boolean isTrue(CompletableFuture<T> cond) {
if (!cond.isDone()) {
@ -68,4 +73,12 @@ public class AsyncWrappedPcodeArithmetic<T> implements PcodeArithmetic<Completab
}
return arithmetic.isTrue(cond.getNow(null));
}
@Override
public BigInteger toConcrete(CompletableFuture<T> cond) {
if (!cond.isDone()) {
throw new AssertionError("You need a better 8-ball");
}
return arithmetic.toConcrete(cond.getNow(null));
}
}

View file

@ -19,7 +19,9 @@ import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;
import ghidra.async.AsyncUtils;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.mem.MemBuffer;
public class AsyncWrappedPcodeExecutorStatePiece<A, T>
implements PcodeExecutorStatePiece<CompletableFuture<A>, CompletableFuture<T>> {
@ -30,6 +32,10 @@ public class AsyncWrappedPcodeExecutorStatePiece<A, T>
this.state = state;
}
protected boolean isWriteDone() {
return lastWrite.isDone();
}
protected <U> CompletableFuture<U> nextRead(Supplier<CompletableFuture<U>> next) {
return lastWrite.thenCompose(__ -> next.get()).exceptionally(ex -> null);
}
@ -68,4 +74,12 @@ public class AsyncWrappedPcodeExecutorStatePiece<A, T>
public CompletableFuture<A> longToOffset(AddressSpace space, long l) {
return CompletableFuture.completedFuture(state.longToOffset(space, l));
}
@Override
public MemBuffer getConcreteBuffer(Address address) {
if (!isWriteDone()) {
throw new AssertionError("An async write is still pending");
}
return state.getConcreteBuffer(address);
}
}

View file

@ -39,7 +39,7 @@ public enum TracePcodeUtils {
PcodeExecutorState<CompletableFuture<byte[]>> state;
if (coordinates.getRecorder() == null) {
state = new AsyncWrappedPcodeExecutorState<>(
new TraceBytesPcodeExecutorState(trace, coordinates.getSnap(),
new TraceBytesPcodeExecutorState(trace, coordinates.getViewSnap(),
coordinates.getThread(), coordinates.getFrame()));
}
else {

View file

@ -0,0 +1,86 @@
/* ###
* 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.app.plugin.core.debug.gui.pcode;
import org.junit.*;
import com.google.common.collect.Range;
import ghidra.app.plugin.assembler.Assembler;
import ghidra.app.plugin.assembler.Assemblers;
import ghidra.app.plugin.core.debug.service.tracemgr.DebuggerTraceManagerServicePlugin;
import ghidra.app.services.DebuggerTraceManagerService;
import ghidra.pcode.exec.PcodeExecutor;
import ghidra.pcode.exec.trace.TraceSleighUtils;
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.util.database.UndoableTransaction;
import help.screenshot.GhidraScreenShotGenerator;
public class DebuggerPcodeStepperPluginScreenShots extends GhidraScreenShotGenerator {
DebuggerTraceManagerService traceManager;
DebuggerPcodeStepperPlugin pcodePlugin;
DebuggerPcodeStepperProvider pcodeProvider;
ToyDBTraceBuilder tb;
@Before
public void setUpMine() throws Throwable {
traceManager = addPlugin(tool, DebuggerTraceManagerServicePlugin.class);
pcodePlugin = addPlugin(tool, DebuggerPcodeStepperPlugin.class);
pcodeProvider = waitForComponentProvider(DebuggerPcodeStepperProvider.class);
tb = new ToyDBTraceBuilder("echo", ToyProgramBuilder._X64);
}
@After
public void tearDownMine() {
tb.close();
}
@Test
public void testCaptureDebuggerPcodeStepperPlugin() throws Throwable {
try (UndoableTransaction tid = tb.startTransaction()) {
long snap0 = tb.trace.getTimeManager().createSnapshot("First").getKey();
tb.trace.getMemoryManager()
.addRegion("[echo:.text]", Range.atLeast(snap0),
tb.range(0x00400000, 0x0040ffff), TraceMemoryFlag.READ,
TraceMemoryFlag.EXECUTE);
TraceThread thread = tb.getOrAddThread("[1]", snap0);
PcodeExecutor<byte[]> exe =
TraceSleighUtils.buildByteExecutor(tb.trace, snap0, thread, 0);
exe.executeLine("RIP = 0x00400000");
exe.executeLine("RSP = 0x0010fff8");
Assembler asm = Assemblers.getAssembler(tb.trace.getFixedProgramView(snap0));
asm.assemble(tb.addr(0x00400000), "SUB RSP,0x40");
traceManager.openTrace(tb.trace);
traceManager.activateThread(thread);
traceManager.activateTime(TraceSchedule.parse("0:.t0-7"));
pcodeProvider.mainPanel.setDividerLocation(0.4);
captureIsolatedProvider(pcodeProvider, 900, 300);
}
}
}

View file

@ -22,6 +22,7 @@ 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.util.database.UndoableTransaction;
import help.screenshot.GhidraScreenShotGenerator;
@ -65,18 +66,18 @@ public class DebuggerTimePluginScreenShots extends GhidraScreenShotGenerator {
snap = tb.trace.getTimeManager().createSnapshot("Thread BREAKPOINT_HIT");
snap.setEventThread(thread);
snap.setRealTime(fakeClock);
snap.setTicks(1);
fakeClock += 2300;
snap = tb.trace.getTimeManager().createSnapshot("Thread STEP_COMPLETED");
snap.setEventThread(thread);
snap.setRealTime(fakeClock);
snap.setTicks(1);
snap.setSchedule(TraceSchedule.parse(snap.getKey() - 1 + ":1"));
fakeClock += 444;
snap = tb.trace.getTimeManager().createSnapshot("Thread STEP_COMPLETED");
snap.setEventThread(thread);
snap.setRealTime(fakeClock);
snap.setSchedule(TraceSchedule.parse(snap.getKey() - 1 + ":1"));
fakeClock += 100;
}

View file

@ -448,28 +448,28 @@ public class DebuggerThreadsProviderTest extends AbstractGhidraHeadedDebuggerGUI
@Test
public void testActionStepTraceBackward() throws Exception {
assertFalse(threadsProvider.actionStepTraceBackward.isEnabled());
assertFalse(threadsProvider.actionStepSnapBackward.isEnabled());
createAndOpenTrace();
addThreads();
traceManager.activateTrace(tb.trace);
waitForSwing();
assertFalse(threadsProvider.actionStepTraceBackward.isEnabled());
assertFalse(threadsProvider.actionStepSnapBackward.isEnabled());
try (UndoableTransaction tid = tb.startTransaction()) {
tb.trace.getTimeManager().getSnapshot(10, true);
}
waitForDomainObject(tb.trace);
assertFalse(threadsProvider.actionStepTraceBackward.isEnabled());
assertFalse(threadsProvider.actionStepSnapBackward.isEnabled());
traceManager.activateSnap(2);
waitForSwing();
assertTrue(threadsProvider.actionStepTraceBackward.isEnabled());
assertTrue(threadsProvider.actionStepSnapBackward.isEnabled());
performAction(threadsProvider.actionStepTraceBackward);
performAction(threadsProvider.actionStepSnapBackward);
waitForSwing();
assertEquals(1, traceManager.getCurrentSnap());
@ -477,32 +477,32 @@ public class DebuggerThreadsProviderTest extends AbstractGhidraHeadedDebuggerGUI
@Test
public void testActionStepTraceForward() throws Exception {
assertFalse(threadsProvider.actionStepTraceForward.isEnabled());
assertFalse(threadsProvider.actionStepSnapForward.isEnabled());
createAndOpenTrace();
addThreads();
traceManager.activateTrace(tb.trace);
waitForSwing();
assertFalse(threadsProvider.actionStepTraceForward.isEnabled());
assertFalse(threadsProvider.actionStepSnapForward.isEnabled());
try (UndoableTransaction tid = tb.startTransaction()) {
tb.trace.getTimeManager().getSnapshot(10, true);
}
waitForDomainObject(tb.trace);
assertTrue(threadsProvider.actionStepTraceForward.isEnabled());
assertTrue(threadsProvider.actionStepSnapForward.isEnabled());
performAction(threadsProvider.actionStepTraceForward);
performAction(threadsProvider.actionStepSnapForward);
waitForSwing();
assertEquals(1, traceManager.getCurrentSnap());
assertTrue(threadsProvider.actionStepTraceForward.isEnabled());
assertTrue(threadsProvider.actionStepSnapForward.isEnabled());
traceManager.activateSnap(10);
waitForSwing();
assertFalse(threadsProvider.actionStepTraceForward.isEnabled());
assertFalse(threadsProvider.actionStepSnapForward.isEnabled());
}
@Test

View file

@ -26,6 +26,7 @@ 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.util.database.UndoableTransaction;
@ -44,11 +45,21 @@ public class DebuggerTimeProviderTest extends AbstractGhidraHeadedDebuggerGUITes
DBTraceTimeManager timeManager = tb.trace.getTimeManager();
try (UndoableTransaction tid = tb.startTransaction()) {
TraceSnapshot first = timeManager.createSnapshot("First");
first.setTicks(123);
Calendar c = Calendar.getInstance(); // System time zone
c.set(2020, 0, 1, 9, 0, 0);
first.setRealTime(c.getTimeInMillis());
timeManager.getSnapshot(10, true).setDescription("Snap 10");
TraceSnapshot second = timeManager.getSnapshot(10, true);
second.setDescription("Snap 10");
second.setSchedule(TraceSchedule.parse("0:5,t1-5"));
}
}
protected void addScratchSnapshot() {
DBTraceTimeManager timeManager = tb.trace.getTimeManager();
try (UndoableTransaction tid = tb.startTransaction()) {
TraceSnapshot scratch = timeManager.getSnapshot(Long.MIN_VALUE, true);
scratch.setDescription("Scratch");
scratch.setSchedule(TraceSchedule.parse("0:t0-5"));
}
}
@ -64,13 +75,13 @@ public class DebuggerTimeProviderTest extends AbstractGhidraHeadedDebuggerGUITes
SnapshotRow firstRow = snapsDisplayed.get(0);
assertEquals(0, firstRow.getSnap());
assertEquals("First", firstRow.getDescription());
assertEquals(123, firstRow.getTicks());
assertEquals("0", firstRow.getSchedule()); // Snap 0 has "0" schedule
assertEquals("Jan 01, 2020 09:00 AM", firstRow.getTimeStamp());
SnapshotRow secondRow = snapsDisplayed.get(1);
assertEquals(10, secondRow.getSnap());
assertEquals("Snap 10", secondRow.getDescription());
assertEquals(0, secondRow.getTicks());
assertEquals("0:5,t1-5", secondRow.getSchedule());
// Timestamp is left unchecked, since default is current time
}
@ -289,4 +300,86 @@ public class DebuggerTimeProviderTest extends AbstractGhidraHeadedDebuggerGUITes
assertNull(timeProvider.snapshotFilterPanel.getSelectedItem());
}
@Test
public void testAddScratchThenActivateIsHidden() throws Exception {
createSnaplessTrace();
traceManager.openTrace(tb.trace);
addSnapshots();
addScratchSnapshot();
waitForDomainObject(tb.trace);
traceManager.activateTrace(tb.trace);
waitForSwing();
List<SnapshotRow> data = timeProvider.snapshotTableModel.getModelData();
assertEquals(2, data.size());
for (SnapshotRow row : data) {
assertTrue(row.getSnap() >= 0);
}
}
@Test
public void testActiveThenAddScratchIsHidden() throws Exception {
createSnaplessTrace();
traceManager.openTrace(tb.trace);
addSnapshots();
waitForDomainObject(tb.trace);
traceManager.activateTrace(tb.trace);
waitForSwing();
assertEquals(2, timeProvider.snapshotTableModel.getModelData().size());
addScratchSnapshot();
waitForDomainObject(tb.trace);
List<SnapshotRow> data = timeProvider.snapshotTableModel.getModelData();
assertEquals(2, data.size());
for (SnapshotRow row : data) {
assertTrue(row.getSnap() >= 0);
}
}
@Test
public void testAddScratchThenActivateThenToggleIsShown() throws Exception {
createSnaplessTrace();
traceManager.openTrace(tb.trace);
addSnapshots();
addScratchSnapshot();
waitForDomainObject(tb.trace);
traceManager.activateTrace(tb.trace);
waitForSwing();
assertEquals(true, timeProvider.hideScratch);
assertEquals(2, timeProvider.snapshotTableModel.getModelData().size());
performAction(timeProvider.actionHideScratch);
assertEquals(false, timeProvider.hideScratch);
assertEquals(3, timeProvider.snapshotTableModel.getModelData().size());
performAction(timeProvider.actionHideScratch);
assertEquals(true, timeProvider.hideScratch);
assertEquals(2, timeProvider.snapshotTableModel.getModelData().size());
}
@Test
public void testToggleThenAddScratchThenActivateIsShown() throws Exception {
performAction(timeProvider.actionHideScratch);
createSnaplessTrace();
traceManager.openTrace(tb.trace);
addSnapshots();
addScratchSnapshot();
waitForDomainObject(tb.trace);
traceManager.activateTrace(tb.trace);
waitForSwing();
assertEquals(false, timeProvider.hideScratch);
assertEquals(3, timeProvider.snapshotTableModel.getModelData().size());
}
}

View file

@ -20,6 +20,7 @@ import static org.junit.Assert.*;
import java.io.File;
import java.util.*;
import org.apache.commons.lang3.tuple.Pair;
import org.junit.Before;
import org.junit.Test;
@ -314,7 +315,7 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
public void testAddMappingThenTranslateTraceViewToStaticEmpty() throws Exception {
addMapping();
Map<Program, AddressSetView> views =
Map<Program, Pair<Long, AddressSetView>> views =
mappingService.getOpenMappedViews(tb.trace, new AddressSet(), 0);
assertTrue(views.isEmpty());
}
@ -335,9 +336,12 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
// After
set.add(dynSpace.getAddress(0xbadbadbadL), dynSpace.getAddress(0xbadbadbadL + 0xff));
Map<Program, AddressSetView> views = mappingService.getOpenMappedViews(tb.trace, set, 0);
Map<Program, Pair<Long, AddressSetView>> views =
mappingService.getOpenMappedViews(tb.trace, set, 0);
assertEquals(1, views.size());
AddressSetView inStatic = views.get(program);
Pair<Long, AddressSetView> pair = views.get(program);
assertEquals(0x100000, pair.getLeft().longValue());
AddressSetView inStatic = pair.getRight();
assertEquals(3, inStatic.getNumAddressRanges());
AddressSet expected = new AddressSet();
expected.add(stSpace.getAddress(0x00200000), stSpace.getAddress(0x002000ff));
@ -352,7 +356,7 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
copyTrace();
add2ndMapping();
Map<TraceSnap, AddressSetView> views =
Map<TraceSnap, Pair<Long, AddressSetView>> views =
mappingService.getOpenMappedViews(program, new AddressSet());
assertTrue(views.isEmpty());
}
@ -375,12 +379,13 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
// After
set.add(stSpace.getAddress(0xbadbadbadL), stSpace.getAddress(0xbadbadbadL + 0xff));
Map<TraceSnap, AddressSetView> views = mappingService.getOpenMappedViews(program, set);
Map<TraceSnap, Pair<Long, AddressSetView>> views =
mappingService.getOpenMappedViews(program, set);
Msg.info(this, views);
assertEquals(2, views.size());
AddressSetView in1st = views.get(new DefaultTraceSnap(tb.trace, 0));
AddressSetView in1st = views.get(new DefaultTraceSnap(tb.trace, 0)).getRight();
assertEquals(5, in1st.getNumAddressRanges());
AddressSetView in2nd = views.get(new DefaultTraceSnap(copy, 0));
AddressSetView in2nd = views.get(new DefaultTraceSnap(copy, 0)).getRight();
assertEquals(3, in2nd.getNumAddressRanges());
AddressSet expectedIn1st = new AddressSet();

View file

@ -90,7 +90,7 @@ public class TraceRecorderAsyncPcodeExecTest extends AbstractGhidraHeadedDebugge
Trace trace = recorder.getTrace();
Language language = trace.getBaseLanguage();
SleighProgram prog = SleighProgramCompiler.compileProgram((SleighLanguage) language, "test",
PcodeProgram prog = SleighProgramCompiler.compileProgram((SleighLanguage) language, "test",
List.of("r2 = r0 + r1;"), SleighUseropLibrary.NIL);
TraceRecorderAsyncPcodeExecutorState asyncState =

View file

@ -28,4 +28,5 @@ dependencies {
annotationProcessor project(':AnnotationValidator')
testCompile project(':Base')
testRuntime project(':ARM') // For its emulator state modifier
}

View file

@ -23,11 +23,14 @@ import org.apache.commons.lang3.tuple.Pair;
import ghidra.generic.util.datastruct.SemisparseByteArray;
import ghidra.pcode.exec.*;
import ghidra.pcode.utils.Utils;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
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.util.DefaultTraceTimeViewport;
public class TraceBytesPcodeExecutorState
extends AbstractLongOffsetPcodeExecutorState<byte[], TraceMemorySpace> {
@ -38,12 +41,17 @@ public class TraceBytesPcodeExecutorState
private TraceThread thread;
private int frame;
private final DefaultTraceTimeViewport viewport;
public TraceBytesPcodeExecutorState(Trace trace, long snap, TraceThread thread, int frame) {
super(trace.getBaseLanguage(), BytesPcodeArithmetic.forLanguage(trace.getBaseLanguage()));
this.trace = trace;
this.snap = snap;
this.thread = thread;
this.frame = frame;
this.viewport = new DefaultTraceTimeViewport(trace);
this.viewport.setSnap(snap);
}
public PcodeExecutorState<Pair<byte[], TraceMemoryState>> withMemoryState() {
@ -81,6 +89,7 @@ public class TraceBytesPcodeExecutorState
public void setSnap(long snap) {
this.snap = snap;
this.viewport.setSnap(snap);
}
public long getSnap() {
@ -147,10 +156,15 @@ public class TraceBytesPcodeExecutorState
@Override
protected byte[] getFromSpace(TraceMemorySpace space, long offset, int size) {
ByteBuffer buf = ByteBuffer.allocate(size);
int read = space.getBytes(snap, space.getAddressSpace().getAddress(offset), buf);
int read = space.getViewBytes(snap, space.getAddressSpace().getAddress(offset), buf);
if (read != size) {
throw new RuntimeException("Could not read full value from trace");
}
return buf.array();
}
@Override
public MemBuffer getConcreteBuffer(Address address) {
return trace.getMemoryManager().getBufferAt(snap, address);
}
}

View file

@ -0,0 +1,257 @@
/* ###
* 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.pcode.exec.trace;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
import com.google.common.collect.*;
import com.google.common.primitives.UnsignedLong;
import ghidra.generic.util.datastruct.SemisparseByteArray;
import ghidra.pcode.exec.AbstractLongOffsetPcodeExecutorState;
import ghidra.pcode.exec.BytesPcodeArithmetic;
import ghidra.pcode.exec.trace.TraceCachedWriteBytesPcodeExecutorState.CachedSpace;
import ghidra.pcode.utils.Utils;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.mem.MemBuffer;
import ghidra.program.model.mem.Memory;
import ghidra.trace.model.Trace;
import ghidra.trace.model.memory.TraceMemorySpace;
import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.util.MemBufferAdapter;
import ghidra.util.MathUtilities;
/**
* A state which reads bytes from a trace, but caches writes internally.
*
* <p>
* This provides for "read-only" emulation on a trace. Writes do not affect the source trace, but
* rather are cached in this state. If desired, those cached writes can be written back out at a
* later time.
*/
public class TraceCachedWriteBytesPcodeExecutorState
extends AbstractLongOffsetPcodeExecutorState<byte[], CachedSpace> {
protected class StateMemBuffer implements MemBufferAdapter {
protected final Address address;
protected final CachedSpace source;
public StateMemBuffer(Address address, CachedSpace source) {
this.address = address;
this.source = source;
}
@Override
public Address getAddress() {
return address;
}
@Override
public Memory getMemory() {
return null;
}
@Override
public boolean isBigEndian() {
return trace.getBaseLanguage().isBigEndian();
}
@Override
public int getBytes(ByteBuffer buffer, int addressOffset) {
byte[] data = source.read(address.getOffset() + addressOffset, buffer.remaining());
buffer.put(data);
return data.length;
}
}
protected final Map<AddressSpace, CachedSpace> spaces = new HashMap<>();
protected final Trace trace;
protected final long snap;
protected final TraceThread thread;
protected final int frame;
public TraceCachedWriteBytesPcodeExecutorState(Trace trace, long snap, TraceThread thread,
int frame) {
super(trace.getBaseLanguage(), BytesPcodeArithmetic.forLanguage(trace.getBaseLanguage()));
this.trace = trace;
this.snap = snap;
this.thread = thread;
this.frame = frame;
}
protected static class CachedSpace {
protected final SemisparseByteArray cache = new SemisparseByteArray();
protected final RangeSet<UnsignedLong> written = TreeRangeSet.create();
protected final AddressSpace space;
protected final TraceMemorySpace source;
protected final long snap;
public CachedSpace(AddressSpace space, TraceMemorySpace source, long snap) {
this.space = space;
this.source = source;
this.snap = snap;
}
public void write(long offset, byte[] val) {
cache.putData(offset, val);
UnsignedLong uLoc = UnsignedLong.fromLongBits(offset);
UnsignedLong uEnd = UnsignedLong.fromLongBits(offset + val.length);
written.add(Range.closedOpen(uLoc, uEnd));
}
public static long lower(Range<UnsignedLong> rng) {
return rng.lowerBoundType() == BoundType.CLOSED
? rng.lowerEndpoint().longValue()
: rng.lowerEndpoint().longValue() + 1;
}
public static long upper(Range<UnsignedLong> rng) {
return rng.upperBoundType() == BoundType.CLOSED
? rng.upperEndpoint().longValue()
: rng.upperEndpoint().longValue() - 1;
}
public byte[] read(long offset, int size) {
if (source != null) {
// TODO: Warn or bail when reading UNKNOWN bytes
// NOTE: Not going to worry about gaps here:
RangeSet<UnsignedLong> uninitialized =
cache.getUninitialized(offset, offset + size);
if (!uninitialized.isEmpty()) {
Range<UnsignedLong> toRead = uninitialized.span();
assert toRead.hasUpperBound() && toRead.hasLowerBound();
long lower = lower(toRead);
long upper = upper(toRead);
ByteBuffer buf = ByteBuffer.allocate((int) (upper - lower + 1));
source.getBytes(snap, space.getAddress(lower), buf);
cache.putData(lower, buf.array());
}
}
byte[] data = new byte[size];
cache.getData(offset, data);
return data;
}
// Must already have started a transaction
protected void writeDown(Trace trace, long snap, TraceThread thread, int frame) {
if (space.isUniqueSpace()) {
return;
}
byte[] data = new byte[4096];
ByteBuffer buf = ByteBuffer.wrap(data);
TraceMemorySpace mem =
TraceSleighUtils.getSpaceForExecution(space, trace, thread, frame, true);
for (Range<UnsignedLong> range : written.asRanges()) {
assert range.lowerBoundType() == BoundType.CLOSED;
assert range.upperBoundType() == BoundType.OPEN;
long lower = range.lowerEndpoint().longValue();
long fullLen = range.upperEndpoint().longValue() - lower;
while (fullLen > 0) {
int len = MathUtilities.unsignedMin(data.length, fullLen);
cache.getData(lower, data, 0, len);
buf.position(0);
buf.limit(len);
mem.putBytes(snap, space.getAddress(lower), buf);
lower += len;
fullLen -= len;
}
}
}
}
public Trace getTrace() {
return trace;
}
public long getSnap() {
return snap;
}
public TraceThread getThread() {
return thread;
}
public int getFrame() {
return frame;
}
/**
* Write the accumulated writes into the given trace
*
* <p>
* NOTE: This method requires a transaction to have already been started on the destination
* trace.
*
* @param trace the trace to modify
* @param snap the snap within the trace
* @param thread the thread to take register writes
* @param frame the frame for register writes
*/
public void writeCacheDown(Trace trace, long snap, TraceThread thread, int frame) {
if (trace.getBaseLanguage() != language) {
throw new IllegalArgumentException("Destination trace must be same language as source");
}
for (CachedSpace cached : spaces.values()) {
cached.writeDown(trace, snap, thread, frame);
}
}
@Override
protected long offsetToLong(byte[] offset) {
return Utils.bytesToLong(offset, offset.length, language.isBigEndian());
}
@Override
public byte[] longToOffset(AddressSpace space, long l) {
return arithmetic.fromConst(l, space.getPointerSize());
}
@Override
protected CachedSpace getForSpace(AddressSpace space, boolean toWrite) {
return spaces.computeIfAbsent(space, s -> {
TraceMemorySpace tms = s.isUniqueSpace() ? null
: TraceSleighUtils.getSpaceForExecution(s, trace, thread, frame, false);
return new CachedSpace(s, tms, snap);
});
}
@Override
protected void setInSpace(CachedSpace space, long offset, int size, byte[] val) {
assert size == val.length;
space.write(offset, val);
}
@Override
protected byte[] getFromSpace(CachedSpace space, long offset, int size) {
byte[] read = space.read(offset, size);
if (read.length != size) {
Address addr = space.space.getAddress(offset);
throw new UnknownStatePcodeExecutionException("Incomplete read (" + read.length +
" of " + size + " bytes)", language, addr.add(read.length), size - read.length);
}
return read;
}
@Override
public MemBuffer getConcreteBuffer(Address address) {
return new StateMemBuffer(address, getForSpace(address.getAddressSpace(), false));
}
}

View file

@ -15,6 +15,8 @@
*/
package ghidra.pcode.exec.trace;
import java.math.BigInteger;
import ghidra.pcode.exec.PcodeArithmetic;
import ghidra.pcode.opbehavior.BinaryOpBehavior;
import ghidra.pcode.opbehavior.UnaryOpBehavior;
@ -43,8 +45,18 @@ public enum TraceMemoryStatePcodeArithmetic implements PcodeArithmetic<TraceMemo
return TraceMemoryState.KNOWN;
}
@Override
public TraceMemoryState fromConst(BigInteger value, int size) {
return TraceMemoryState.KNOWN;
}
@Override
public boolean isTrue(TraceMemoryState cond) {
throw new AssertionError("Cannot decide branches using TraceMemoryState");
}
@Override
public BigInteger toConcrete(TraceMemoryState value) {
throw new AssertionError("Cannot make TraceMemoryState a 'concrete value'");
}
}

View file

@ -23,10 +23,12 @@ import com.google.common.primitives.UnsignedLong;
import ghidra.pcode.exec.AbstractLongOffsetPcodeExecutorStatePiece;
import ghidra.pcode.utils.Utils;
import ghidra.program.model.address.*;
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.util.DefaultTraceTimeViewport;
public class TraceMemoryStatePcodeExecutorStatePiece extends
AbstractLongOffsetPcodeExecutorStatePiece<byte[], TraceMemoryState, TraceMemorySpace> {
@ -37,6 +39,8 @@ public class TraceMemoryStatePcodeExecutorStatePiece extends
private TraceThread thread;
private int frame;
private final DefaultTraceTimeViewport viewport;
public TraceMemoryStatePcodeExecutorStatePiece(Trace trace, long snap, TraceThread thread,
int frame) {
super(trace.getBaseLanguage(), TraceMemoryStatePcodeArithmetic.INSTANCE);
@ -44,6 +48,9 @@ public class TraceMemoryStatePcodeExecutorStatePiece extends
this.snap = snap;
this.thread = thread;
this.frame = frame;
this.viewport = new DefaultTraceTimeViewport(trace);
this.viewport.setSnap(snap);
}
public Trace getTrace() {
@ -52,6 +59,7 @@ public class TraceMemoryStatePcodeExecutorStatePiece extends
public void setSnap(long snap) {
this.snap = snap;
this.viewport.setSnap(snap);
}
public long getSnap() {
@ -137,7 +145,15 @@ public class TraceMemoryStatePcodeExecutorStatePiece extends
@Override
protected TraceMemoryState getFromSpace(TraceMemorySpace space, long offset, int size) {
AddressSet set = new AddressSet(range(space.getAddressSpace(), offset, size));
set.delete(space.getAddressesWithState(snap, set, s -> s == TraceMemoryState.KNOWN));
for (long snap : viewport.getOrderedSnaps()) {
set.delete(
space.getAddressesWithState(snap, set, state -> state == TraceMemoryState.KNOWN));
}
return set.isEmpty() ? TraceMemoryState.KNOWN : TraceMemoryState.UNKNOWN;
}
@Override
public MemBuffer getConcreteBuffer(Address address) {
throw new AssertionError("Cannot make TraceMemoryState into a concrete buffer");
}
}

View file

@ -0,0 +1,101 @@
/* ###
* 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.pcode.exec.trace;
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.pcode.emu.AbstractPcodeEmulator;
import ghidra.pcode.emu.PcodeThread;
import ghidra.pcode.exec.PcodeExecutorState;
import ghidra.pcode.exec.SleighUseropLibrary;
import ghidra.program.model.lang.Language;
import ghidra.trace.model.Trace;
import ghidra.trace.model.stack.TraceStack;
import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.model.thread.TraceThreadManager;
/**
* An emulator that can read initial state from a trace
*/
public class TracePcodeEmulator extends AbstractPcodeEmulator {
private static SleighLanguage assertSleigh(Language language) {
if (!(language instanceof SleighLanguage)) {
throw new IllegalArgumentException("Emulation requires a sleigh language");
}
return (SleighLanguage) language;
}
protected final Trace trace;
protected final long snap;
public TracePcodeEmulator(Trace trace, long snap, SleighUseropLibrary<byte[]> library) {
super(assertSleigh(trace.getBaseLanguage()), library);
this.trace = trace;
this.snap = snap;
}
public TracePcodeEmulator(Trace trace, long snap) {
this(trace, snap, SleighUseropLibrary.nil());
}
@Override
protected PcodeExecutorState<byte[]> createMemoryState() {
return new TraceCachedWriteBytesPcodeExecutorState(trace, snap, null, 0);
}
@Override
protected PcodeExecutorState<byte[]> createRegisterState(PcodeThread<byte[]> emuThread) {
TraceThread traceThread =
trace.getThreadManager().getLiveThreadByPath(snap, emuThread.getName());
return new TraceCachedWriteBytesPcodeExecutorState(trace, snap, traceThread, 0);
}
/**
* Write the accumulated writes into the given trace at the given snap
*
* <p>
* NOTE: This method requires a transaction to have already been started on the destination
* trace. The destination threads must have equal names/paths at the given threadsSnap. When
* using scratch space, threadsSnap should be the source snap. If populating a new trace,
* threadsSnap should probably be the destination snap.
*
* @param trace the trace to modify
* @param destSnap the destination snap within the trace
* @param threadsSnap the snap at which to find corresponding threads
* @param synthesizeStacks true to synthesize the innermost stack frame of each thread
*/
public void writeDown(Trace trace, long destSnap, long threadsSnap, boolean synthesizeStacks) {
TraceCachedWriteBytesPcodeExecutorState ms =
(TraceCachedWriteBytesPcodeExecutorState) getMemoryState();
ms.writeCacheDown(trace, destSnap, null, 0);
TraceThreadManager threadManager = trace.getThreadManager();
for (PcodeThread<byte[]> emuThread : threads.values()) {
TraceCachedWriteBytesPcodeExecutorState rs =
(TraceCachedWriteBytesPcodeExecutorState) emuThread.getState().getRegisterState();
TraceThread traceThread = threadManager.getLiveThreadByPath(
threadsSnap, emuThread.getName());
if (traceThread == null) {
throw new IllegalArgumentException(
"Given trace does not have thread with name/path '" + emuThread.getName() +
"' at snap " + destSnap);
}
rs.writeCacheDown(trace, destSnap, traceThread, 0);
if (synthesizeStacks) {
TraceStack stack = trace.getStackManager().getStack(traceThread, destSnap, true);
stack.getFrame(0, true).setProgramCounter(emuThread.getCounter());
}
}
}
}

View file

@ -39,7 +39,7 @@ public enum TraceSleighUtils {
if (space.isRegisterSpace()) {
if (thread == null) {
throw new IllegalArgumentException(
"Cannot execute with register context unless a thread is given.");
"Cannot access register unless a thread is given.");
}
return trace.getMemoryManager().getMemoryRegisterSpace(thread, frame, toWrite);
}

View file

@ -0,0 +1,52 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.pcode.exec.trace;
import java.util.Arrays;
import ghidra.pcode.exec.AccessPcodeExecutionException;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.Language;
import ghidra.program.model.lang.Register;
public class UnknownStatePcodeExecutionException extends AccessPcodeExecutionException {
public static String getMessage(Language language, Address address, int size) {
if (address.getAddressSpace().isRegisterSpace()) {
Register reg = language.getRegister(address, size);
if (reg != null) {
return "No recorded value for register " + reg;
}
return "No recorded value for register(s) " +
Arrays.asList(language.getRegisters(address));
}
try {
return "No recorded value for memory at " + new AddressRangeImpl(address, size);
}
catch (AddressOverflowException e) {
throw new AssertionError(e);
}
}
public UnknownStatePcodeExecutionException(Language language, Address address, int size) {
super(getMessage(language, address, size));
}
public UnknownStatePcodeExecutionException(String message, Language language, Address address,
int size) {
super(message + ": " + getMessage(language, address, size));
}
}

View file

@ -185,7 +185,8 @@ public class DBTraceRegisterContextManager extends
public RegisterValue getValueWithDefault(Language language, Register register, long snap,
Address address) {
return delegateRead(address.getAddressSpace(),
m -> m.getValueWithDefault(language, register, snap, address));
m -> m.getValueWithDefault(language, register, snap, address),
() -> getDefaultValue(language, register, address));
}
@Override

View file

@ -29,10 +29,10 @@ import ghidra.trace.database.DBTraceUtils;
import ghidra.trace.database.data.DBTraceDataSettingsAdapter.DBTraceSettingsEntry;
import ghidra.trace.database.map.*;
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.AbstractDBTraceAddressSnapRangePropertyMapData;
import ghidra.trace.database.space.DBTraceSpaceKey;
import ghidra.trace.database.thread.DBTraceThread;
import ghidra.trace.database.thread.DBTraceThreadManager;
import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.util.TraceAddressSpace;
import ghidra.util.database.*;
import ghidra.util.database.annot.*;
import ghidra.util.exception.VersionException;
@ -226,8 +226,8 @@ public class DBTraceDataSettingsAdapter
}
@Override
public DBTraceDataSettingsSpace get(DBTraceSpaceKey key, boolean createIfAbsent) {
return (DBTraceDataSettingsSpace) super.get(key, createIfAbsent);
public DBTraceDataSettingsSpace get(TraceAddressSpace space, boolean createIfAbsent) {
return (DBTraceDataSettingsSpace) super.get(space, createIfAbsent);
}
@Override

View file

@ -25,6 +25,7 @@ import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree;
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.AbstractDBTraceAddressSnapRangePropertyMapData;
import ghidra.trace.database.memory.DBTraceMemorySpace;
import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.util.TraceAddressSpace;
import ghidra.util.LockHold;
import ghidra.util.database.DBCachedObjectStore;
@ -42,6 +43,11 @@ public abstract class AbstractDBTraceCodeUnit<T extends AbstractDBTraceCodeUnit<
this.space = space;
}
@Override
public TraceAddressSpace getTraceSpace() {
return space;
}
@Override
public Address getAddress() {
return getX1();
@ -96,7 +102,7 @@ public abstract class AbstractDBTraceCodeUnit<T extends AbstractDBTraceCodeUnit<
byteCache.limit(Math.min(byteCache.capacity(), end));
// TODO: Retrieve the memory space at code space construction time
DBTraceMemorySpace mem = space.trace.getMemoryManager().get(space, false);
mem.getBytes(getStartSnap(), address.add(byteCache.position()), byteCache);
mem.getViewBytes(getStartSnap(), address.add(byteCache.position()), byteCache);
}
// Copy from the cache
int toCopyFromCache =
@ -110,7 +116,7 @@ public abstract class AbstractDBTraceCodeUnit<T extends AbstractDBTraceCodeUnit<
int startRemains = Math.max(addressOffset, byteCache.position());
DBTraceMemorySpace mem = space.trace.getMemoryManager().get(space, false);
return toCopyFromCache +
mem.getBytes(getStartSnap(), address.add(startRemains), buffer);
mem.getViewBytes(getStartSnap(), address.add(startRemains), buffer);
}
}
}

View file

@ -26,11 +26,9 @@ import ghidra.program.model.lang.Language;
import ghidra.trace.database.DBTrace;
import ghidra.trace.database.data.DBTraceDataSettingsAdapter.DBTraceDataSettingsSpace;
import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.util.DataAdapterFromDataType;
import ghidra.util.LockHold;
public abstract class AbstractDBTraceDataComponent
implements DBTraceDefinedDataAdapter, DataAdapterFromDataType {
public abstract class AbstractDBTraceDataComponent implements DBTraceDefinedDataAdapter {
protected final DBTraceData root;
protected final DBTraceDefinedDataAdapter parent;

View file

@ -50,8 +50,10 @@ import ghidra.trace.database.thread.DBTraceThreadManager;
import ghidra.trace.model.AddressSnap;
import ghidra.trace.model.DefaultAddressSnap;
import ghidra.trace.model.listing.TraceCodeManager;
import ghidra.trace.model.listing.TraceCodeSpace;
import ghidra.trace.model.stack.TraceStackFrame;
import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.util.TraceAddressSpace;
import ghidra.util.*;
import ghidra.util.database.*;
import ghidra.util.database.annot.*;
@ -295,6 +297,11 @@ public class DBTraceCodeManager
return spaceStore.writeLock();
}
@Override
public TraceCodeSpace getCodeSpace(TraceAddressSpace space, boolean createIfAbsent) {
return get(space, createIfAbsent);
}
@Override
public DBTraceCodeSpace getCodeSpace(AddressSpace space, boolean createIfAbsent) {
return getForSpace(space, createIfAbsent);

View file

@ -27,7 +27,6 @@ import ghidra.program.model.lang.Language;
import ghidra.trace.database.DBTraceUtils;
import ghidra.trace.database.data.DBTraceDataSettingsAdapter.DBTraceDataSettingsSpace;
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree;
import ghidra.trace.util.DataAdapterFromDataType;
import ghidra.util.LockHold;
import ghidra.util.database.DBCachedObjectStore;
import ghidra.util.database.DBObjectColumn;
@ -35,8 +34,7 @@ import ghidra.util.database.annot.*;
@DBAnnotatedObjectInfo(version = 0)
public class DBTraceData extends AbstractDBTraceCodeUnit<DBTraceData>
implements DBTraceDefinedDataAdapter, DataAdapterFromDataType {
static final int[] EMPTY_INT_ARRAY = new int[0];
implements DBTraceDefinedDataAdapter {
private static final String TABLE_NAME = "Data";
static final String LANGUAGE_COLUMN_NAME = "Langauge";

View file

@ -20,66 +20,39 @@ import java.util.Collection;
import ghidra.docking.settings.Settings;
import ghidra.docking.settings.SettingsDefinition;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.MutabilitySettingsDefinition;
import ghidra.program.model.symbol.RefType;
import ghidra.program.model.symbol.SourceType;
import ghidra.trace.database.data.DBTraceDataSettingsOperations;
import ghidra.trace.database.symbol.DBTraceReference;
import ghidra.trace.model.listing.TraceCodeManager;
import ghidra.trace.model.listing.TraceData;
import ghidra.trace.model.symbol.TraceReference;
import ghidra.trace.util.*;
import ghidra.util.LockHold;
public interface DBTraceDataAdapter extends DBTraceCodeUnitAdapter, TraceData {
public interface DBTraceDataAdapter extends DBTraceCodeUnitAdapter, DataAdapterMinimal,
DataAdapterFromDataType, DataAdapterFromSettings, TraceData {
static String[] EMPTY_STRING_ARRAY = new String[] {};
default String doToString() {
StringBuilder builder = new StringBuilder();
builder.append(getMnemonicString());
String valueRepresentation = getDefaultValueRepresentation();
if (valueRepresentation != null) {
builder.append(' ');
builder.append(valueRepresentation);
}
return builder.toString();
}
@Override
default String getMnemonicString() {
return getDataType().getMnemonic(this);
}
@Override
DBTraceDataAdapter getRoot();
default String getPrimarySymbolOrDynamicName() {
/** TODO: Use primary symbol or dynamic name as in {@link DataDB#getPathName()} */
return "DAT_" + getAddressString(false, false);
}
@Override
default int getNumOperands() {
return 1;
}
@Override
default TraceReference[] getValueReferences() {
return getOperandReferences(TraceCodeManager.DATA_OP_INDEX);
return (TraceReference[]) DataAdapterMinimal.super.getValueReferences();
}
@Override
default void addValueReference(Address refAddr, RefType type) {
getTrace().getReferenceManager()
.addMemoryReference(getLifespan(), getAddress(), refAddr,
type, SourceType.USER_DEFINED, TraceCodeManager.DATA_OP_INDEX);
type, SourceType.USER_DEFINED, DATA_OP_INDEX);
}
@Override
default void removeValueReference(Address refAddr) {
DBTraceReference ref = getTrace().getReferenceManager()
.getReference(getStartSnap(),
getAddress(), refAddr, TraceCodeManager.DATA_OP_INDEX);
getAddress(), refAddr, DATA_OP_INDEX);
if (ref == null) {
return;
}
@ -198,36 +171,19 @@ public interface DBTraceDataAdapter extends DBTraceCodeUnitAdapter, TraceData {
return space.isEmpty(getLifespan(), getAddress());
}
@Override
default <T extends SettingsDefinition> T getSettingsDefinition(
Class<T> settingsDefinitionClass) {
DataType dt = getBaseDataType();
for (SettingsDefinition def : dt.getSettingsDefinitions()) {
if (settingsDefinitionClass.isAssignableFrom(def.getClass())) {
return settingsDefinitionClass.cast(def);
try (LockHold hold = LockHold.lock(getTrace().getReadWriteLock().readLock())) {
return DataAdapterFromSettings.super.getSettingsDefinition(settingsDefinitionClass);
}
}
return null;
}
@Override
default boolean hasMutability(int mutabilityType) {
try (LockHold hold = LockHold.lock(getTrace().getReadWriteLock().readLock())) {
MutabilitySettingsDefinition def =
getSettingsDefinition(MutabilitySettingsDefinition.class);
if (def != null) {
return def.getChoice(this) == mutabilityType;
return DataAdapterFromSettings.super.hasMutability(mutabilityType);
}
return false;
}
}
@Override
default boolean isConstant() {
return hasMutability(MutabilitySettingsDefinition.CONSTANT);
}
@Override
default boolean isVolatile() {
return hasMutability(MutabilitySettingsDefinition.VOLATILE);
}
@Override

View file

@ -15,8 +15,11 @@
*/
package ghidra.trace.database.listing;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.*;
import ghidra.program.model.data.DataType;
import ghidra.trace.model.ImmutableTraceAddressSnapRange;
import ghidra.trace.model.TraceAddressSnapRange;
import ghidra.trace.util.TraceAddressSpace;
public class DBTraceDataArrayElementComponent extends AbstractDBTraceDataComponent {
public DBTraceDataArrayElementComponent(DBTraceData root, DBTraceDefinedDataAdapter parent,
@ -24,6 +27,11 @@ public class DBTraceDataArrayElementComponent extends AbstractDBTraceDataCompone
super(root, parent, index, address, dataType, length);
}
@Override
public TraceAddressSpace getTraceSpace() {
return parent.getTraceSpace();
}
@Override
public String getFieldName() {
return "[" + index + "]";
@ -33,4 +41,16 @@ public class DBTraceDataArrayElementComponent extends AbstractDBTraceDataCompone
public String getFieldSyntax() {
return getFieldName();
}
@Override
public AddressRange getRange() {
// TODO: Cache this?
return new AddressRangeImpl(getMinAddress(), getMaxAddress());
}
@Override
public TraceAddressSnapRange getBounds() {
// TODO: Cache this?
return new ImmutableTraceAddressSnapRange(getMinAddress(), getMaxAddress(), getLifespan());
}
}

View file

@ -15,8 +15,11 @@
*/
package ghidra.trace.database.listing;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.*;
import ghidra.program.model.data.DataTypeComponent;
import ghidra.trace.model.ImmutableTraceAddressSnapRange;
import ghidra.trace.model.TraceAddressSnapRange;
import ghidra.trace.util.TraceAddressSpace;
public class DBTraceDataCompositeFieldComponent extends AbstractDBTraceDataComponent {
protected final DataTypeComponent dtc;
@ -27,6 +30,11 @@ public class DBTraceDataCompositeFieldComponent extends AbstractDBTraceDataCompo
this.dtc = dtc;
}
@Override
public TraceAddressSpace getTraceSpace() {
return parent.getTraceSpace();
}
@Override
public String getFieldName() {
String fieldName = dtc.getFieldName();
@ -40,4 +48,16 @@ public class DBTraceDataCompositeFieldComponent extends AbstractDBTraceDataCompo
public String getFieldSyntax() {
return "." + getFieldName();
}
@Override
public AddressRange getRange() {
// TODO: Cache this?
return new AddressRangeImpl(getMinAddress(), getMaxAddress());
}
@Override
public TraceAddressSnapRange getBounds() {
// TODO: Cache this?
return new ImmutableTraceAddressSnapRange(getMinAddress(), getMaxAddress(), getLifespan());
}
}

View file

@ -22,7 +22,6 @@ import java.util.*;
import com.google.common.collect.Range;
import db.DBRecord;
import ghidra.lifecycle.Unfinished;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.*;
import ghidra.program.model.listing.ContextChangeException;
@ -47,7 +46,7 @@ import ghidra.util.database.annot.*;
@DBAnnotatedObjectInfo(version = 0)
public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstruction> implements
TraceInstruction, InstructionAdapterFromPrototype, InstructionContext, Unfinished {
TraceInstruction, InstructionAdapterFromPrototype, InstructionContext {
private static final Address[] EMPTY_ADDRESS_ARRAY = new Address[] {};
private static final String TABLE_NAME = "Instructions";
@ -347,7 +346,7 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
return EMPTY_ADDRESS_ARRAY;
}
Collection<? extends DBTraceReference> refs =
refSpace.getFlowRefrencesFrom(getStartSnap(), getAddress());
refSpace.getFlowReferencesFrom(getStartSnap(), getAddress());
if (refs.isEmpty()) {
return EMPTY_ADDRESS_ARRAY;
}
@ -461,7 +460,7 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
update(FLAGS_COLUMN);
DBTraceReferenceSpace refSpace = space.referenceManager.get(space, true);
for (DBTraceReference ref : refSpace.getFlowRefrencesFrom(getStartSnap(), getX1())) {
for (DBTraceReference ref : refSpace.getFlowReferencesFrom(getStartSnap(), getX1())) {
if (!isSameFlowType(origFlowType, ref.getReferenceType())) {
continue;
}

View file

@ -22,8 +22,7 @@ import java.util.List;
import com.google.common.collect.Range;
import ghidra.docking.settings.Settings;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.address.*;
import ghidra.program.model.data.DataType;
import ghidra.program.model.lang.Language;
import ghidra.program.model.listing.Data;
@ -33,11 +32,12 @@ import ghidra.trace.database.data.DBTraceDataSettingsOperations;
import ghidra.trace.database.memory.DBTraceMemorySpace;
import ghidra.trace.database.space.DBTraceSpaceKey;
import ghidra.trace.database.thread.DBTraceThread;
import ghidra.trace.model.ImmutableTraceAddressSnapRange;
import ghidra.trace.model.TraceAddressSnapRange;
import ghidra.trace.model.listing.TraceData;
import ghidra.trace.util.DataAdapterFromDataType;
import ghidra.trace.util.TraceAddressSpace;
public class UndefinedDBTraceData
implements DBTraceDataAdapter, DataAdapterFromDataType, DBTraceSpaceKey {
public class UndefinedDBTraceData implements DBTraceDataAdapter, DBTraceSpaceKey {
protected final DBTrace trace;
protected final long snap;
protected final Range<Long> lifespan;
@ -55,6 +55,11 @@ public class UndefinedDBTraceData
this.frameLevel = frameLevel;
}
@Override
public TraceAddressSpace getTraceSpace() {
return this;
}
@Override
public AddressSpace getAddressSpace() {
return address.getAddressSpace();
@ -75,6 +80,18 @@ public class UndefinedDBTraceData
return trace.getBaseLanguage();
}
@Override
public AddressRange getRange() {
// TODO: Cache this?
return new AddressRangeImpl(getMinAddress(), getMaxAddress());
}
@Override
public TraceAddressSnapRange getBounds() {
// TODO: Cache this?
return new ImmutableTraceAddressSnapRange(getMinAddress(), getMaxAddress(), getLifespan());
}
@Override
public Range<Long> getLifespan() {
return lifespan;
@ -207,7 +224,7 @@ public class UndefinedDBTraceData
@Override
public int[] getComponentPath() {
return DBTraceData.EMPTY_INT_ARRAY;
return EMPTY_INT_ARRAY;
}
@Override

View file

@ -165,7 +165,8 @@ public class DBTraceAddressSnapRangePropertyMapAddressSetView<T> extends Abstrac
: new AddressRangeImpl(fullSpace.getMinAddress(), start);
Iterator<Entry<TraceAddressSnapRange, T>> mapIt = map
.reduce(TraceAddressSnapRangeQuery.intersecting(within, Range.all())
.starting(forward ? Rectangle2DDirection.LEFTMOST
.starting(forward
? Rectangle2DDirection.LEFTMOST
: Rectangle2DDirection.RIGHTMOST))
.orderedEntries()
.iterator();

View file

@ -505,6 +505,12 @@ public class DBTraceAddressSnapRangePropertyMapTree<T, DR extends AbstractDBTrac
Rectangle2DDirection.TOPMOST, TraceAddressSnapRangeQuery::new);
}
public static TraceAddressSnapRangeQuery mostRecent(Address address, Range<Long> span) {
return intersecting(
new ImmutableTraceAddressSnapRange(address, span),
Rectangle2DDirection.TOPMOST, TraceAddressSnapRangeQuery::new);
}
public static TraceAddressSnapRangeQuery equalTo(TraceAddressSnapRange shape) {
return equalTo(shape, null, TraceAddressSnapRangeQuery::new);
}

View file

@ -56,7 +56,7 @@ public class DBTraceMemBuffer implements MemBufferAdapter {
@Override
public int getBytes(ByteBuffer buffer, int offset) {
try {
return space.getBytes(snap, start.addNoWrap(offset), buffer);
return space.getViewBytes(snap, start.addNoWrap(offset), buffer);
}
catch (AddressOverflowException e) {
return 0;

View file

@ -211,6 +211,11 @@ public class DBTraceMemoryManager
return delegateRead(address.getAddressSpace(), m -> m.getState(snap, address));
}
@Override
public Entry<Long, TraceMemoryState> getViewState(long snap, Address address) {
return delegateRead(address.getAddressSpace(), m -> m.getViewState(snap, address));
}
@Override
public Entry<TraceAddressSnapRange, TraceMemoryState> getMostRecentStateEntry(long snap,
Address address) {
@ -218,6 +223,13 @@ public class DBTraceMemoryManager
m -> m.getMostRecentStateEntry(snap, address));
}
@Override
public Entry<TraceAddressSnapRange, TraceMemoryState> getViewMostRecentStateEntry(long snap,
Address address) {
return delegateRead(address.getAddressSpace(),
m -> m.getViewMostRecentStateEntry(snap, address));
}
@Override
public AddressSetView getAddressesWithState(long snap, AddressSetView set,
Predicate<TraceMemoryState> predicate) {
@ -267,6 +279,16 @@ public class DBTraceMemoryManager
});
}
@Override
public int getViewBytes(long snap, Address start, ByteBuffer buf) {
return delegateReadI(start.getAddressSpace(), m -> m.getViewBytes(snap, start, buf), () -> {
Address max = start.getAddressSpace().getMaxAddress();
int len = MathUtilities.unsignedMin(buf.remaining(), max.subtract(start));
buf.position(buf.position() + len);
return len;
});
}
@Override
public void removeBytes(long snap, Address start, int len) {
delegateDeleteV(start.getAddressSpace(), m -> m.removeBytes(snap, start, len));

View file

@ -46,7 +46,9 @@ import ghidra.trace.model.*;
import ghidra.trace.model.Trace.*;
import ghidra.trace.model.memory.*;
import ghidra.trace.util.TraceChangeRecord;
import ghidra.trace.util.TraceViewportSpanIterator;
import ghidra.util.LockHold;
import ghidra.util.MathUtilities;
import ghidra.util.database.*;
import ghidra.util.database.spatial.rect.Rectangle2DDirection;
import ghidra.util.exception.DuplicateNameException;
@ -329,8 +331,7 @@ public class DBTraceMemorySpace implements Unfinished, TraceMemorySpace, DBTrace
protected void checkState(TraceMemoryState state) {
if (state == null || state == TraceMemoryState.UNKNOWN) {
throw new IllegalArgumentException(
"User cannot erase memory state without removing bytes");
throw new IllegalArgumentException("Cannot erase memory state without removing bytes");
}
}
@ -376,6 +377,26 @@ public class DBTraceMemorySpace implements Unfinished, TraceMemorySpace, DBTrace
return state == null ? TraceMemoryState.UNKNOWN : state;
}
@Override
public Entry<Long, TraceMemoryState> getViewState(long snap, Address address) {
TraceViewportSpanIterator spit = new TraceViewportSpanIterator(trace, snap);
while (spit.hasNext()) {
Range<Long> span = spit.next();
TraceMemoryState state = getState(span.upperEndpoint(), address);
switch (state) {
case KNOWN:
case ERROR:
return Map.entry(span.upperEndpoint(), state);
default: // fall through
}
// Only the snap with the schedule specified gets the source snap's states
if (span.upperEndpoint() - span.lowerEndpoint() > 0) {
return Map.entry(snap, TraceMemoryState.UNKNOWN);
}
}
return Map.entry(snap, TraceMemoryState.UNKNOWN);
}
@Override
public Entry<TraceAddressSnapRange, TraceMemoryState> getMostRecentStateEntry(long snap,
Address address) {
@ -383,6 +404,22 @@ public class DBTraceMemorySpace implements Unfinished, TraceMemorySpace, DBTrace
TraceAddressSnapRangeQuery.mostRecent(address, snap)).firstEntry();
}
@Override
public Entry<TraceAddressSnapRange, TraceMemoryState> getViewMostRecentStateEntry(long snap,
Address address) {
TraceViewportSpanIterator spit = new TraceViewportSpanIterator(trace, snap);
while (spit.hasNext()) {
Range<Long> span = spit.next();
Entry<TraceAddressSnapRange, TraceMemoryState> entry =
stateMapSpace.reduce(TraceAddressSnapRangeQuery.mostRecent(address, span))
.firstEntry();
if (entry != null) {
return entry;
}
}
return null;
}
@Override
public AddressSetView getAddressesWithState(long snap, Predicate<TraceMemoryState> predicate) {
return new DBTraceAddressSnapRangePropertyMapAddressSetView<>(space, lock,
@ -658,6 +695,58 @@ public class DBTraceMemorySpace implements Unfinished, TraceMemorySpace, DBTrace
return result;
}
@Override
public int getViewBytes(long snap, Address start, ByteBuffer buf) {
AddressRange toRead;
int len = MathUtilities.unsignedMin(buf.remaining(),
start.getAddressSpace().getMaxAddress().subtract(start) + 1);
try {
toRead = new AddressRangeImpl(start, len);
}
catch (AddressOverflowException e) {
throw new AssertionError(e);
}
Map<AddressRange, Long> sources = new TreeMap<>();
AddressSet remains = new AddressSet(toRead);
TraceViewportSpanIterator spit = new TraceViewportSpanIterator(trace, snap);
spans: while (spit.hasNext()) {
Range<Long> span = spit.next();
Iterator<AddressRange> arit =
getAddressesWithState(span, s -> s == TraceMemoryState.KNOWN).iterator(start, true);
while (arit.hasNext()) {
AddressRange rng = arit.next();
if (rng.getMinAddress().compareTo(toRead.getMaxAddress()) > 0) {
break;
}
for (AddressRange sub : remains.intersectRange(rng.getMinAddress(),
rng.getMaxAddress())) {
sources.put(sub, span.upperEndpoint());
}
remains.delete(rng);
if (remains.isEmpty()) {
break spans;
}
}
}
int lim = buf.limit();
int pos = buf.position();
for (Map.Entry<AddressRange, Long> ent : sources.entrySet()) {
AddressRange rng = ent.getKey();
int offset = (int) rng.getMinAddress().subtract(toRead.getMinAddress());
int length = (int) rng.getLength();
buf.position(pos + offset);
buf.limit(pos + offset + length);
int read = getBytes(ent.getValue(), rng.getMinAddress(), buf);
if (read < length) {
break;
}
}
// We "got it all", even if there were gaps in "KNOWN"
buf.limit(lim);
buf.position(pos + len);
return len;
}
@Override
public Address findBytes(long snap, AddressRange range, ByteBuffer data, ByteBuffer mask,
boolean forward, TaskMonitor monitor) {

View file

@ -15,8 +15,12 @@
*/
package ghidra.trace.database.program;
import java.nio.ByteBuffer;
import java.util.*;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.RemovalNotification;
import com.google.common.collect.Iterators;
import com.google.common.collect.Range;
import generic.NestedIterator;
@ -32,17 +36,21 @@ import ghidra.program.model.symbol.Namespace;
import ghidra.program.model.symbol.SourceType;
import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.program.model.util.PropertyMap;
import ghidra.trace.database.DBTrace;
import ghidra.trace.database.listing.UndefinedDBTraceData;
import ghidra.trace.database.memory.DBTraceMemoryRegion;
import ghidra.trace.database.memory.DBTraceMemorySpace;
import ghidra.trace.database.symbol.DBTraceFunctionSymbol;
import ghidra.trace.model.Trace;
import ghidra.trace.model.listing.TraceCodeOperations;
import ghidra.trace.model.listing.TraceData;
import ghidra.trace.database.thread.DBTraceThread;
import ghidra.trace.model.*;
import ghidra.trace.model.listing.*;
import ghidra.trace.model.map.TracePropertyMap;
import ghidra.trace.model.program.TraceProgramView;
import ghidra.trace.model.program.TraceProgramViewListing;
import ghidra.trace.model.symbol.TraceFunctionSymbol;
import ghidra.util.IntersectionAddressSetView;
import ghidra.util.LockHold;
import ghidra.trace.util.*;
import ghidra.util.*;
import ghidra.util.AddressIteratorAdapter;
import ghidra.util.exception.*;
import ghidra.util.task.TaskMonitor;
@ -50,72 +58,19 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
public static final String[] EMPTY_STRING_ARRAY = new String[] {};
public static final String TREE_NAME = "Trace Tree";
protected static class WrappingCodeUnitIterator implements CodeUnitIterator {
protected final Iterator<? extends CodeUnit> it;
public WrappingCodeUnitIterator(Iterator<? extends CodeUnit> it) {
this.it = it;
protected class DBTraceProgramViewUndefinedData extends UndefinedDBTraceData {
public DBTraceProgramViewUndefinedData(DBTrace trace, long snap, Address address,
DBTraceThread thread, int frameLevel) {
super(trace, snap, address, thread, frameLevel);
}
@Override
public Iterator<CodeUnit> iterator() {
return this;
public int getBytes(ByteBuffer buffer, int addressOffset) {
DBTraceMemorySpace mem = trace.getMemoryManager().get(this, false);
if (mem == null) {
// TODO: 0-fill instead? Will need to check memory space bounds.
}
@Override
public boolean hasNext() {
return it.hasNext();
}
@Override
public CodeUnit next() {
return it.next();
}
}
protected static class WrappingInstructionIterator implements InstructionIterator {
protected final Iterator<? extends Instruction> it;
public WrappingInstructionIterator(Iterator<? extends Instruction> it) {
this.it = it;
}
@Override
public Iterator<Instruction> iterator() {
return this;
}
@Override
public boolean hasNext() {
return it.hasNext();
}
@Override
public Instruction next() {
return it.next();
}
}
protected static class WrappingDataIterator implements DataIterator {
protected final Iterator<? extends Data> it;
public WrappingDataIterator(Iterator<? extends Data> it) {
this.it = it;
}
@Override
public Iterator<Data> iterator() {
return this;
}
@Override
public boolean hasNext() {
return it.hasNext();
}
@Override
public Data next() {
return it.next();
return mem.getViewBytes(program.snap, address.add(addressOffset), buffer);
}
}
@ -126,6 +81,14 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
protected final Map<DBTraceMemoryRegion, DBTraceProgramViewFragment> fragmentsByRegion =
new HashMap<>();
protected final Map<AddressSnap, UndefinedDBTraceData> undefinedCache =
CacheBuilder.newBuilder()
.removalListener(
this::undefinedRemovedFromCache)
.weakValues()
.build()
.asMap();
public AbstractDBTraceProgramViewListing(DBTraceProgramView program,
TraceCodeOperations codeOperations) {
this.program = program;
@ -134,6 +97,11 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
this.rootModule = new DBTraceProgramViewRootModule(this);
}
private void undefinedRemovedFromCache(
RemovalNotification<AddressSnap, UndefinedDBTraceData> rn) {
// Do nothing
}
@Override
public TraceProgramView getProgram() {
return program;
@ -149,32 +117,241 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
return program.snap;
}
protected <T extends TraceCodeUnit> T getTopCode(
java.util.function.Function<Long, T> codeFunc) {
return program.viewport.getTop(s -> {
T cu = codeFunc.apply(s);
if (cu != null && program.isCodeVisible(cu, cu.getLifespan())) {
return cu;
}
return null;
});
}
protected TraceCodeUnit orUndef(TraceCodeUnit cu, Address address) {
if (cu != null) {
return cu;
}
return doCreateUndefinedUnit(address);
}
protected TraceData orUndefData(TraceData data, Address address) {
return (TraceData) orUndef(data, address);
}
protected TraceData reqUndef(TraceCodeUnit cu, Address address) {
if (cu != null) {
return null;
}
return doCreateUndefinedUnit(address);
}
protected <T> T next(Iterator<T> it) {
if (it.hasNext()) {
return it.next();
}
return null;
}
protected Comparator<CodeUnit> getUnitComparator(boolean forward) {
return forward
? (u1, u2) -> u1.getMinAddress().compareTo(u2.getMinAddress())
: (u1, u2) -> -u1.getMinAddress().compareTo(u2.getMinAddress());
}
protected <T extends TraceCodeUnit> Iterator<T> getTopCodeIterator(
java.util.function.Function<Long, Iterator<T>> iterFunc, boolean forward) {
return Iterators.filter(
program.viewport.mergedIterator(iterFunc, getUnitComparator(forward)),
cu -> program.isCodeVisible(cu, cu.getLifespan()));
}
protected AddressSet getAddressSet(Address start, boolean forward) {
AddressFactory factory = program.getAddressFactory();
AddressSet all = program.allAddresses;
return forward
? factory.getAddressSet(start, all.getMaxAddress())
: factory.getAddressSet(all.getMinAddress(), start);
}
protected UndefinedDBTraceData doCreateUndefinedUnit(Address address) {
return undefinedCache.computeIfAbsent(new DefaultAddressSnap(address, program.snap),
ot -> new DBTraceProgramViewUndefinedData(program.trace, program.snap, address, null,
0));
}
protected Iterator<? extends TraceInstruction> getInstructionIterator(Address start,
boolean forward) {
return getTopCodeIterator(
s -> codeOperations.instructions().get(s, start, forward).iterator(), forward);
}
protected Iterator<? extends TraceInstruction> getInstructionIterator(AddressSetView set,
boolean forward) {
return getTopCodeIterator(
s -> codeOperations.instructions().get(s, set, forward).iterator(), forward);
}
protected Iterator<? extends TraceInstruction> getInstructionIterator(boolean forward) {
return getTopCodeIterator(
s -> codeOperations.instructions().get(s, forward).iterator(), forward);
}
protected Iterator<? extends TraceData> getDefinedDataIterator(Address start, boolean forward) {
return getTopCodeIterator(
s -> codeOperations.definedData().get(s, start, forward).iterator(), forward);
}
protected Iterator<? extends TraceData> getDefinedDataIterator(AddressSetView set,
boolean forward) {
return getTopCodeIterator(
s -> codeOperations.definedData().get(s, set, forward).iterator(), forward);
}
protected Iterator<? extends TraceData> getDefinedDataIterator(boolean forward) {
return getTopCodeIterator(
s -> codeOperations.definedData().get(s, forward).iterator(), forward);
}
protected Iterator<? extends TraceCodeUnit> getDefinedUnitIterator(Address start,
boolean forward) {
return getTopCodeIterator(
s -> codeOperations.definedUnits().get(s, start, forward).iterator(), forward);
}
protected Iterator<? extends TraceCodeUnit> getDefinedUnitIterator(AddressSetView set,
boolean forward) {
return getTopCodeIterator(
s -> codeOperations.definedUnits().get(s, set, forward).iterator(), forward);
}
protected Iterator<TraceData> getUndefinedDataIterator(Address start, boolean forward) {
AddressSet set = getAddressSet(start, forward);
Address defStart = start;
if (forward) {
CodeUnit defUnit =
getTopCode(s -> codeOperations.definedUnits().getContaining(s, start));
if (defUnit != null) {
defStart = defUnit.getMinAddress();
}
}
Iterator<AddressRange> defIter = Iterators.transform(
getDefinedUnitIterator(defStart, forward), u -> u.getRange());
AddressRangeIterator undefIter =
AddressRangeIterators.subtract(set.iterator(forward), defIter, start, forward);
AddressIteratorAdapter undefAddrIter = new AddressIteratorAdapter(undefIter, forward);
return Iterators.transform(undefAddrIter.iterator(), a -> doCreateUndefinedUnit(a));
}
protected AddressRangeIterator getUndefinedRangeIterator(AddressSetView set, boolean forward) {
Iterator<AddressRange> defIter = Iterators.transform(
getDefinedUnitIterator(set, forward), u -> u.getRange());
return AddressRangeIterators.subtract(set.iterator(forward), defIter,
forward ? set.getMinAddress() : set.getMaxAddress(), forward);
}
protected boolean isUndefinedRange(long snap, AddressRange range) {
if (codeOperations.undefinedData().coversRange(Range.singleton(snap), range)) {
return true;
}
TraceCodeUnit minUnit =
codeOperations.definedUnits().getContaining(snap, range.getMinAddress());
if (minUnit != null && program.isCodeVisible(minUnit, minUnit.getLifespan())) {
return false;
}
TraceCodeUnit maxUnit =
codeOperations.definedUnits().getContaining(snap, range.getMaxAddress());
if (maxUnit != null && program.isCodeVisible(maxUnit, maxUnit.getLifespan())) {
return false;
}
return true;
}
protected Iterator<TraceData> getUndefinedDataIterator(AddressSetView set, boolean forward) {
AddressRangeIterator undefIter = getUndefinedRangeIterator(set, forward);
AddressIteratorAdapter undefAddrIter = new AddressIteratorAdapter(undefIter, forward);
return Iterators.transform(undefAddrIter.iterator(), a -> doCreateUndefinedUnit(a));
}
protected Iterator<TraceCodeUnit> getCodeUnitIterator(AddressSetView set, boolean forward) {
return new MergeSortingIterator<>(List.of(
getDefinedUnitIterator(set, forward),
getUndefinedDataIterator(set, forward)),
getUnitComparator(forward));
}
protected Iterator<TraceCodeUnit> getCodeUnitIterator(Address start, boolean forward) {
return new MergeSortingIterator<>(List.of(
getDefinedUnitIterator(start, forward),
getUndefinedDataIterator(start, forward)),
getUnitComparator(forward));
}
protected Iterator<TraceCodeUnit> getCodeUnitIterator(boolean forward) {
AddressSetView set = program.allAddresses;
return getCodeUnitIterator(forward ? set.getMinAddress() : set.getMaxAddress(), forward);
}
protected Iterator<TraceData> getDataIterator(AddressSetView set, boolean forward) {
return new MergeSortingIterator<>(List.of(
getDefinedDataIterator(set, forward),
getUndefinedDataIterator(set, forward)),
getUnitComparator(forward));
}
protected Iterator<TraceData> getDataIterator(Address start, boolean forward) {
return new MergeSortingIterator<>(List.of(
getDefinedDataIterator(start, forward),
getUndefinedDataIterator(start, forward)),
getUnitComparator(forward));
}
protected Iterator<TraceData> getDataIterator(boolean forward) {
AddressSetView set = program.allAddresses;
return getDataIterator(forward ? set.getMinAddress() : set.getMaxAddress(), forward);
}
@Override
public CodeUnit getCodeUnitAt(Address addr) {
return codeOperations.codeUnits().getAt(program.snap, addr);
CodeUnit containing = getCodeUnitContaining(addr);
if (containing == null) {
return doCreateUndefinedUnit(addr);
}
if (!containing.getMinAddress().equals(addr)) {
return null;
}
return containing;
}
@Override
public CodeUnit getCodeUnitContaining(Address addr) {
return codeOperations.codeUnits().getContaining(program.snap, addr);
try (LockHold hold = program.trace.lockRead()) {
return orUndef(getTopCode(s -> codeOperations.definedUnits().getContaining(s, addr)),
addr);
}
}
@Override
public CodeUnit getCodeUnitAfter(Address addr) {
return codeOperations.codeUnits().getAfter(program.snap, addr);
addr = addr.next();
try (LockHold hold = program.trace.lockRead()) {
return addr == null ? null : next(getCodeUnitIterator(addr, true));
}
}
@Override
public CodeUnit getCodeUnitBefore(Address addr) {
return codeOperations.codeUnits().getBefore(program.snap, addr);
addr = addr.previous();
try (LockHold hold = program.trace.lockRead()) {
return addr == null ? null : next(getCodeUnitIterator(addr, false));
}
}
@Override
public CodeUnitIterator getCodeUnitIterator(String property, boolean forward) {
// HACK
if (CodeUnit.INSTRUCTION_PROPERTY.equals(property)) {
return new WrappingCodeUnitIterator(
codeOperations.instructions().get(program.snap, forward).iterator());
return new WrappingCodeUnitIterator(getInstructionIterator(forward));
}
// TODO: Other "special" property types
@ -184,28 +361,19 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
if (map == null) {
return new WrappingCodeUnitIterator(Collections.emptyIterator());
}
// TODO: The property map doesn't heed forking.
return new WrappingCodeUnitIterator(NestedIterator.start(
map.getAddressSetView(Range.singleton(program.snap)).iterator(forward),
rng -> program.trace.getCodeManager()
.codeUnits()
.get(program.snap, rng, forward)
.iterator()));
}
protected static AddressRange fixRange(AddressRange range, Address start, boolean forward) {
if (!range.contains(start)) {
return range;
}
return forward ? new AddressRangeImpl(start, range.getMaxAddress())
: new AddressRangeImpl(range.getMinAddress(), start);
rng -> getTopCodeIterator(
s -> codeOperations.codeUnits().get(s, rng, forward).iterator(),
forward)));
}
@Override
public CodeUnitIterator getCodeUnitIterator(String property, Address addr, boolean forward) {
// HACK
if (CodeUnit.INSTRUCTION_PROPERTY.equals(property)) {
return new WrappingCodeUnitIterator(
codeOperations.instructions().get(program.snap, addr, forward).iterator());
return new WrappingCodeUnitIterator(getInstructionIterator(addr, forward));
}
// TODO: Other "special" property types
@ -215,12 +383,12 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
if (map == null) {
return new WrappingCodeUnitIterator(Collections.emptyIterator());
}
// TODO: The property map doesn't heed forking.
return new WrappingCodeUnitIterator(NestedIterator.start(
map.getAddressSetView(Range.singleton(program.snap)).iterator(addr, forward),
rng -> program.trace.getCodeManager()
.codeUnits()
.get(program.snap, fixRange(rng, addr, forward), forward)
.iterator()));
rng -> getTopCodeIterator(
s -> codeOperations.codeUnits().get(s, rng, forward).iterator(),
forward)));
}
@Override
@ -228,8 +396,7 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
boolean forward) {
// HACK
if (CodeUnit.INSTRUCTION_PROPERTY.equals(property)) {
return new WrappingCodeUnitIterator(
codeOperations.instructions().get(program.snap, addrSet, forward).iterator());
return new WrappingCodeUnitIterator(getInstructionIterator(addrSet, forward));
}
// TODO: Other "special" property types
@ -239,13 +406,13 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
if (map == null) {
return new WrappingCodeUnitIterator(Collections.emptyIterator());
}
// TODO: The property map doesn't heed forking.
return new WrappingCodeUnitIterator(NestedIterator.start(
new IntersectionAddressSetView(map.getAddressSetView(Range.singleton(program.snap)),
addrSet).iterator(forward),
rng -> program.trace.getCodeManager()
.codeUnits()
.get(program.snap, rng, forward)
.iterator()));
rng -> getTopCodeIterator(
s -> codeOperations.codeUnits().get(s, rng, forward).iterator(),
forward)));
}
@Override
@ -269,7 +436,10 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
@Override
public String getComment(int commentType, Address address) {
return program.trace.getCommentAdapter().getComment(program.snap, address, commentType);
try (LockHold hold = program.trace.lockRead()) {
return program.viewport.getTop(
s -> program.trace.getCommentAdapter().getComment(s, address, commentType));
}
}
@Override
@ -281,190 +451,227 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
@Override
public CodeUnitIterator getCodeUnits(boolean forward) {
return new WrappingCodeUnitIterator(
codeOperations.codeUnits().get(program.snap, forward).iterator());
return new WrappingCodeUnitIterator(getCodeUnitIterator(forward));
}
@Override
public CodeUnitIterator getCodeUnits(Address start, boolean forward) {
return new WrappingCodeUnitIterator(
codeOperations.codeUnits().get(program.snap, start, forward).iterator());
return new WrappingCodeUnitIterator(getCodeUnitIterator(start, forward));
}
@Override
public CodeUnitIterator getCodeUnits(AddressSetView addressSet, boolean forward) {
return new WrappingCodeUnitIterator(
codeOperations.codeUnits().get(program.snap, addressSet, forward).iterator());
return new WrappingCodeUnitIterator(getCodeUnitIterator(addressSet, forward));
}
@Override
public Instruction getInstructionAt(Address addr) {
return codeOperations.instructions().getAt(program.snap, addr);
try (LockHold hold = program.trace.lockRead()) {
return getTopCode(s -> codeOperations.instructions().getAt(s, addr));
}
}
@Override
public Instruction getInstructionContaining(Address addr) {
return codeOperations.instructions().getContaining(program.snap, addr);
try (LockHold hold = program.trace.lockRead()) {
return getTopCode(s -> codeOperations.instructions().getContaining(s, addr));
}
}
@Override
public Instruction getInstructionAfter(Address addr) {
return codeOperations.instructions().getAfter(program.snap, addr);
addr = addr.next();
try (LockHold hold = program.trace.lockRead()) {
return addr == null ? null : next(getInstructionIterator(addr, true));
}
}
@Override
public Instruction getInstructionBefore(Address addr) {
return codeOperations.instructions().getBefore(program.snap, addr);
addr = addr.previous();
try (LockHold hold = program.trace.lockRead()) {
return addr == null ? null : next(getInstructionIterator(addr, false));
}
}
@Override
public InstructionIterator getInstructions(boolean forward) {
return new WrappingInstructionIterator(
codeOperations.instructions().get(program.snap, forward).iterator());
return new WrappingInstructionIterator(getInstructionIterator(forward));
}
@Override
public InstructionIterator getInstructions(Address start, boolean forward) {
return new WrappingInstructionIterator(
codeOperations.instructions().get(program.snap, start, forward).iterator());
return new WrappingInstructionIterator(getInstructionIterator(start, forward));
}
@Override
public InstructionIterator getInstructions(AddressSetView addressSet, boolean forward) {
return new WrappingInstructionIterator(
codeOperations.instructions().get(program.snap, addressSet, forward).iterator());
return new WrappingInstructionIterator(getInstructionIterator(addressSet, forward));
}
@Override
public Data getDataAt(Address addr) {
return codeOperations.data().getAt(program.snap, addr);
CodeUnit containing = getCodeUnitContaining(addr);
if (containing == null) {
return doCreateUndefinedUnit(addr);
}
if (!(containing instanceof Data)) {
return null;
}
if (!containing.getMinAddress().equals(addr)) {
return null;
}
return (Data) containing;
}
@Override
public Data getDataContaining(Address addr) {
return codeOperations.data().getContaining(program.snap, addr);
CodeUnit cu = getCodeUnitContaining(addr);
if (cu instanceof Data) {
return (Data) cu;
}
return null;
}
@Override
public Data getDataAfter(Address addr) {
return codeOperations.data().getAfter(program.snap, addr);
addr = addr.next();
try (LockHold hold = program.trace.lockRead()) {
return addr == null ? null : next(getDataIterator(addr, true));
}
}
@Override
public Data getDataBefore(Address addr) {
return codeOperations.data().getBefore(program.snap, addr);
addr = addr.previous();
try (LockHold hold = program.trace.lockRead()) {
return addr == null ? null : next(getDataIterator(addr, false));
}
}
@Override
public DataIterator getData(boolean forward) {
return new WrappingDataIterator(
codeOperations.data().get(program.snap, forward).iterator());
return new WrappingDataIterator(getDataIterator(forward));
}
@Override
public DataIterator getData(Address start, boolean forward) {
return new WrappingDataIterator(
codeOperations.data().get(program.snap, start, forward).iterator());
return new WrappingDataIterator(getDataIterator(start, forward));
}
@Override
public DataIterator getData(AddressSetView addressSet, boolean forward) {
return new WrappingDataIterator(
codeOperations.data().get(program.snap, addressSet, forward).iterator());
return new WrappingDataIterator(getDataIterator(addressSet, forward));
}
@Override
public Data getDefinedDataAt(Address addr) {
return codeOperations.definedData().getAt(program.snap, addr);
try (LockHold hold = program.trace.lockRead()) {
return getTopCode(s -> codeOperations.definedData().getAt(s, addr));
}
}
@Override
public Data getDefinedDataContaining(Address addr) {
return codeOperations.definedData().getContaining(program.snap, addr);
try (LockHold hold = program.trace.lockRead()) {
return getTopCode(s -> codeOperations.definedData().getContaining(s, addr));
}
}
@Override
public Data getDefinedDataAfter(Address addr) {
return codeOperations.definedData().getAfter(program.snap, addr);
addr = addr.next();
try (LockHold hold = program.trace.lockRead()) {
return addr == null ? null : next(getDefinedDataIterator(addr, true));
}
}
@Override
public Data getDefinedDataBefore(Address addr) {
return codeOperations.definedData().getBefore(program.snap, addr);
addr = addr.previous();
try (LockHold hold = program.trace.lockRead()) {
return addr == null ? null : next(getDefinedDataIterator(addr, false));
}
}
@Override
public DataIterator getDefinedData(boolean forward) {
return new WrappingDataIterator(
codeOperations.definedData().get(program.snap, forward).iterator());
return new WrappingDataIterator(getDefinedDataIterator(forward));
}
@Override
public DataIterator getDefinedData(Address start, boolean forward) {
return new WrappingDataIterator(
codeOperations.definedData().get(program.snap, start, forward).iterator());
return new WrappingDataIterator(getDefinedDataIterator(start, forward));
}
@Override
public DataIterator getDefinedData(AddressSetView addressSet, boolean forward) {
return new WrappingDataIterator(
codeOperations.definedData().get(program.snap, addressSet, forward).iterator());
return new WrappingDataIterator(getDefinedDataIterator(addressSet, forward));
}
@Override
public Data getUndefinedDataAt(Address addr) {
return codeOperations.undefinedData().getAt(program.snap, addr);
try (LockHold hold = program.trace.lockRead()) {
return reqUndef(getTopCode(s -> codeOperations.definedUnits().getContaining(s, addr)),
addr);
}
}
@Override
public Data getUndefinedDataAfter(Address addr, TaskMonitor monitor) {
return codeOperations.undefinedData().getAfter(program.snap, addr);
addr = addr.next();
try (LockHold hold = program.trace.lockRead()) {
return addr == null ? null : next(getUndefinedDataIterator(addr, true));
}
}
@Override
public Data getUndefinedDataBefore(Address addr, TaskMonitor monitor) {
return codeOperations.undefinedData().getBefore(program.snap, addr);
addr = addr.previous();
try (LockHold hold = program.trace.lockRead()) {
return addr == null ? null : next(getUndefinedDataIterator(addr, false));
}
}
@Override
public Data getFirstUndefinedData(AddressSetView addressSet, TaskMonitor monitor) {
try (LockHold hold = program.trace.lockRead()) {
for (TraceData u : codeOperations.undefinedData().get(program.snap, addressSet, true)) {
return u;
}
return null;
return next(getUndefinedDataIterator(addressSet, true));
}
}
/**
* {@inheritDoc}
*
* @implNote This could technically use a (lazy) view; however, to be consistent with
* expectations established by {@link ProgramDB}, it constructs the actual set, and
* permits cancellation by the monitor.
* @implNote This could maybe use a (lazy) view; however, to be consistent with expectations
* established by {@link ProgramDB}, it constructs the actual set, and permits
* cancellation by the monitor.
*/
@Override
public AddressSet getUndefinedRanges(AddressSetView set, boolean initializedMemoryOnly,
TaskMonitor monitor) throws CancelledException {
AddressSet result = new AddressSet();
for (AddressRange range : set) {
for (AddressRange und : codeOperations.undefinedData()
.getAddressSetView(program.snap, range)) {
for (AddressRange range : getUndefinedRangeIterator(set, true)) {
result.add(range);
monitor.checkCanceled();
result.add(und.intersect(range));
}
}
return result;
}
@Override
public CodeUnit getDefinedCodeUnitAfter(Address addr) {
return codeOperations.definedUnits().getAfter(program.snap, addr);
addr = addr.next();
try (LockHold hold = program.trace.lockRead()) {
return next(getDefinedUnitIterator(addr, true));
}
}
@Override
public CodeUnit getDefinedCodeUnitBefore(Address addr) {
return codeOperations.definedUnits().getBefore(program.snap, addr);
addr = addr.previous();
try (LockHold hold = program.trace.lockRead()) {
return next(getDefinedUnitIterator(addr, false));
}
}
@Override
@ -561,8 +768,8 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
@Override
public ProgramFragment getFragment(String treeName, Address addr) {
DBTraceMemoryRegion region =
program.trace.getMemoryManager().getRegionContaining(program.snap, addr);
DBTraceMemoryRegion region = program.memory.getTopRegion(
s -> program.trace.getMemoryManager().getRegionContaining(s, addr));
if (region == null) {
return null;
}
@ -580,8 +787,8 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
@Override
public ProgramFragment getFragment(String treeName, String name) {
DBTraceMemoryRegion region =
program.trace.getMemoryManager().getLiveRegionByPath(program.snap, name);
DBTraceMemoryRegion region = program.memory.getTopRegion(
s -> program.trace.getMemoryManager().getLiveRegionByPath(s, name));
if (region == null) {
return null;
}
@ -648,8 +855,8 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
@Override
public long getNumInstructions() {
// TODO: See getNumCodeUnits
return Long.MAX_VALUE;
// TODO: See getNumCodeUnits... Why was this Long.MAX_VALUE before?
return codeOperations.instructions().size();
}
@Override

View file

@ -18,7 +18,6 @@ package ghidra.trace.database.program;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.*;
import com.google.common.cache.RemovalNotification;
@ -31,12 +30,14 @@ import ghidra.trace.database.memory.*;
import ghidra.trace.model.Trace;
import ghidra.trace.model.program.TraceProgramView;
import ghidra.trace.model.program.TraceProgramViewMemory;
import ghidra.trace.util.MemoryAdapter;
import ghidra.util.MathUtilities;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.NotFoundException;
import ghidra.util.task.TaskMonitor;
public abstract class AbstractDBTraceProgramViewMemory implements TraceProgramViewMemory {
public abstract class AbstractDBTraceProgramViewMemory
implements TraceProgramViewMemory, MemoryAdapter {
protected final DBTraceProgramView program;
protected final DBTraceMemoryManager memoryManager;
@ -95,7 +96,7 @@ public abstract class AbstractDBTraceProgramViewMemory implements TraceProgramVi
public AddressSetView getExecuteSet() {
AddressSet result = new AddressSet();
for (DBTraceMemoryRegion region : memoryManager.getRegionsInternal()) {
if (!region.isExecute()) {
if (!region.isExecute() || !program.isRegionVisible(region, region.getLifespan())) {
continue;
}
result.add(region.getRange());
@ -247,6 +248,8 @@ public abstract class AbstractDBTraceProgramViewMemory implements TraceProgramVi
if (space == null) {
continue;
}
// TODO: findBytes must heed fork, or there should exist a variant that does....
// Lest I have to implement the forked search here.
Address found =
space.findBytes(snap, range, bufBytes, bufMasks, forward, monitor);
if (found != null) {
@ -265,11 +268,6 @@ public abstract class AbstractDBTraceProgramViewMemory implements TraceProgramVi
return block.getByte(addr);
}
@Override
public int getBytes(Address addr, byte[] dest) throws MemoryAccessException {
return getBytes(addr, dest, 0, dest.length);
}
@Override
public int getBytes(Address addr, byte[] dest, int destIndex, int size)
throws MemoryAccessException {
@ -283,109 +281,6 @@ public abstract class AbstractDBTraceProgramViewMemory implements TraceProgramVi
return block.getBytes(addr, dest, destIndex, size);
}
protected ByteBuffer mustRead(Address addr, int length, boolean bigEndian)
throws MemoryAccessException {
ByteBuffer buf = ByteBuffer.allocate(length);
if (getBytes(addr, buf.array()) != length) {
throw new MemoryAccessException();
}
buf.order(bigEndian ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN);
return buf;
}
@Override
public short getShort(Address addr) throws MemoryAccessException {
return mustRead(addr, Short.BYTES, true).getShort(0);
}
@Override
public short getShort(Address addr, boolean bigEndian) throws MemoryAccessException {
return mustRead(addr, Short.BYTES, bigEndian).getShort(0);
}
@Override
public int getShorts(Address addr, short[] dest) throws MemoryAccessException {
return getShorts(addr, dest, 0, dest.length, true);
}
@Override
public int getShorts(Address addr, short[] dest, int dIndex, int nElem)
throws MemoryAccessException {
return getShorts(addr, dest, dIndex, nElem, true);
}
@Override
public int getShorts(Address addr, short[] dest, int dIndex, int nElem, boolean bigEndian)
throws MemoryAccessException {
ByteBuffer buf = ByteBuffer.allocate(Short.BYTES * nElem);
int got = getBytes(addr, buf.array()) / Short.BYTES;
buf.order(bigEndian ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN);
buf.asShortBuffer().get(dest, dIndex, got);
return got;
}
@Override
public int getInt(Address addr) throws MemoryAccessException {
return mustRead(addr, Integer.BYTES, true).getInt(0);
}
@Override
public int getInt(Address addr, boolean bigEndian) throws MemoryAccessException {
return mustRead(addr, Integer.BYTES, bigEndian).getInt(0);
}
@Override
public int getInts(Address addr, int[] dest) throws MemoryAccessException {
return getInts(addr, dest, 0, dest.length, true);
}
@Override
public int getInts(Address addr, int[] dest, int dIndex, int nElem)
throws MemoryAccessException {
return getInts(addr, dest, dIndex, nElem, true);
}
@Override
public int getInts(Address addr, int[] dest, int dIndex, int nElem, boolean bigEndian)
throws MemoryAccessException {
ByteBuffer buf = ByteBuffer.allocate(Integer.BYTES * nElem);
int got = getBytes(addr, buf.array()) / Integer.BYTES;
buf.order(bigEndian ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN);
buf.asIntBuffer().get(dest, dIndex, got);
return got;
}
@Override
public long getLong(Address addr) throws MemoryAccessException {
return mustRead(addr, Long.BYTES, true).getLong(0);
}
@Override
public long getLong(Address addr, boolean bigEndian) throws MemoryAccessException {
return mustRead(addr, Long.BYTES, bigEndian).getLong(0);
}
@Override
public int getLongs(Address addr, long[] dest) throws MemoryAccessException {
return getLongs(addr, dest, 0, dest.length, true);
}
@Override
public int getLongs(Address addr, long[] dest, int dIndex, int nElem)
throws MemoryAccessException {
return getLongs(addr, dest, dIndex, nElem, true);
}
@Override
public int getLongs(Address addr, long[] dest, int dIndex, int nElem, boolean bigEndian)
throws MemoryAccessException {
ByteBuffer buf = ByteBuffer.allocate(Long.BYTES * nElem);
int got = getBytes(addr, buf.array()) / Long.BYTES;
buf.order(bigEndian ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN);
buf.asLongBuffer().get(dest, dIndex, got);
return got;
}
@Override
public void setByte(Address addr, byte value) throws MemoryAccessException {
DBTraceMemorySpace space = memoryManager.getMemorySpace(addr.getAddressSpace(), true);
@ -394,11 +289,6 @@ public abstract class AbstractDBTraceProgramViewMemory implements TraceProgramVi
}
}
@Override
public void setBytes(Address addr, byte[] source) throws MemoryAccessException {
setBytes(addr, source, 0, source.length);
}
@Override
public void setBytes(Address addr, byte[] source, int sIndex, int size)
throws MemoryAccessException {
@ -408,46 +298,6 @@ public abstract class AbstractDBTraceProgramViewMemory implements TraceProgramVi
}
}
@Override
public void setShort(Address addr, short value) throws MemoryAccessException {
setShort(addr, value, true);
}
@Override
public void setShort(Address addr, short value, boolean bigEndian)
throws MemoryAccessException {
ByteBuffer buf = ByteBuffer.allocate(Short.BYTES);
buf.order(bigEndian ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN);
buf.putShort(value);
setBytes(addr, buf.array());
}
@Override
public void setInt(Address addr, int value) throws MemoryAccessException {
setInt(addr, value, true);
}
@Override
public void setInt(Address addr, int value, boolean bigEndian) throws MemoryAccessException {
ByteBuffer buf = ByteBuffer.allocate(Integer.BYTES);
buf.order(bigEndian ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN);
buf.putInt(value);
setBytes(addr, buf.array());
}
@Override
public void setLong(Address addr, long value) throws MemoryAccessException {
setLong(addr, value, true);
}
@Override
public void setLong(Address addr, long value, boolean bigEndian) throws MemoryAccessException {
ByteBuffer buf = ByteBuffer.allocate(Long.BYTES);
buf.order(bigEndian ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN);
buf.putLong(value);
setBytes(addr, buf.array());
}
@Override
public FileBytes createFileBytes(String filename, long offset, long size, InputStream is,
TaskMonitor monitor) throws IOException, CancelledException {

View file

@ -17,8 +17,9 @@ package ghidra.trace.database.program;
import static ghidra.lifecycle.Unfinished.TODO;
import java.util.Collection;
import java.util.Collections;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import javax.help.UnsupportedOperationException;
@ -175,21 +176,42 @@ public abstract class AbstractDBTraceProgramViewReferenceManager implements Refe
dbRef.setPrimary(isPrimary);
}
@Override
public boolean hasFlowReferencesFrom(Address addr) {
protected boolean any(boolean noSpace, Predicate<Long> predicate) {
if (refs(false) == null) {
return noSpace;
}
for (long s : program.viewport.getOrderedSnaps()) {
if (predicate.test(s)) {
return true;
}
}
return false;
}
return !refs.getFlowRefrencesFrom(program.snap, addr).isEmpty();
protected Collection<Reference> collect(
Function<Long, Collection<? extends Reference>> refFunc) {
if (refs(false) == null) {
return Collections.emptyList();
}
Set<Reference> result = new LinkedHashSet<>();
for (long s : program.viewport.getOrderedSnaps()) {
Collection<? extends Reference> from = refFunc.apply(s);
if (from != null) {
result.addAll(from);
}
}
return result;
}
@Override
public boolean hasFlowReferencesFrom(Address addr) {
return any(false, s -> !refs.getFlowReferencesFrom(s, addr).isEmpty());
}
@Override
public Reference[] getFlowReferencesFrom(Address addr) {
Collection<? extends TraceReference> from = refs(false) == null
? Collections.emptyList()
: refs.getFlowRefrencesFrom(program.snap, addr);
// TODO: Requires two traversals. Not terrible for this size....
return from.toArray(new Reference[from.size()]);
Collection<Reference> result = collect(s -> refs.getFlowReferencesFrom(s, addr));
return result.toArray(new Reference[result.size()]);
}
@Override
@ -199,120 +221,133 @@ public abstract class AbstractDBTraceProgramViewReferenceManager implements Refe
@Override
public ReferenceIterator getReferencesTo(Address addr) {
Collection<? extends TraceReference> to = refs(false) == null
? Collections.emptyList()
: refs.getReferencesTo(program.snap, addr);
return new ReferenceIteratorAdapter(to.iterator());
Collection<Reference> result = collect(s -> refs.getReferencesTo(s, addr));
return new ReferenceIteratorAdapter(result.iterator());
}
protected Comparator<Reference> getReferenceFromComparator(boolean forward) {
return forward
? (r1, r2) -> r1.getFromAddress().compareTo(r2.getFromAddress())
: (r1, r2) -> -r1.getFromAddress().compareTo(r2.getFromAddress());
}
@Override
public ReferenceIterator getReferenceIterator(Address startAddr) {
Collection<? extends TraceReference> from = refs(false) == null
? Collections.emptyList()
: refs.getReferencesFrom(program.snap, startAddr);
return new ReferenceIteratorAdapter(from.iterator());
if (refs(false) == null) {
return new ReferenceIteratorAdapter(Collections.emptyIterator());
}
return new ReferenceIteratorAdapter(
program.viewport.mergedIterator(s -> refs.getReferencesFrom(s, startAddr).iterator(),
getReferenceFromComparator(true)));
}
@Override
public Reference getReference(Address fromAddr, Address toAddr, int opIndex) {
return refs(false) == null
? null
: refs.getReference(program.snap, fromAddr, toAddr, opIndex);
if (refs(false) == null) {
return null;
}
return program.viewport.getTop(s -> refs.getReference(s, fromAddr, toAddr, opIndex));
}
@Override
public Reference[] getReferencesFrom(Address addr) {
Collection<? extends TraceReference> from = refs(false) == null
? Collections.emptyList()
: refs.getReferencesFrom(program.snap, addr);
return from.toArray(new Reference[from.size()]);
Collection<Reference> result = collect(s -> refs.getReferencesFrom(s, addr));
return result.toArray(new Reference[result.size()]);
}
@Override
public Reference[] getReferencesFrom(Address fromAddr, int opIndex) {
Collection<? extends TraceReference> from = refs(false) == null
? Collections.emptyList()
: refs.getReferencesFrom(program.snap, fromAddr, opIndex);
return from.toArray(new Reference[from.size()]);
Collection<Reference> result = collect(s -> refs.getReferencesFrom(s, fromAddr, opIndex));
return result.toArray(new Reference[result.size()]);
}
@Override
public boolean hasReferencesFrom(Address fromAddr, int opIndex) {
return refs(false) == null
? false
: !refs.getReferencesFrom(program.snap, fromAddr, opIndex).isEmpty();
return any(false, s -> !refs.getReferencesFrom(s, fromAddr, opIndex).isEmpty());
}
@Override
public boolean hasReferencesFrom(Address fromAddr) {
return refs(false) == null
? false
: !refs.getReferencesFrom(program.snap, fromAddr).isEmpty();
return any(false, s -> !refs.getReferencesFrom(s, fromAddr).isEmpty());
}
@Override
public Reference getPrimaryReferenceFrom(Address addr, int opIndex) {
return refs(false) == null
? null
: refs.getPrimaryReferenceFrom(program.snap, addr, opIndex);
if (refs(false) == null) {
return null;
}
return program.viewport.getTop(s -> refs.getPrimaryReferenceFrom(s, addr, opIndex));
}
@Override
public AddressIterator getReferenceSourceIterator(Address startAddr, boolean forward) {
return refs(false) == null
? new EmptyAddressIterator()
: refs.getReferenceSources(Range.closed(program.snap, program.snap))
.getAddresses(startAddr, forward);
if (refs(false) == null) {
return new EmptyAddressIterator();
}
return program.viewport.unionedAddresses(
s -> refs.getReferenceSources(Range.singleton(s))).getAddresses(startAddr, forward);
}
@Override
public AddressIterator getReferenceSourceIterator(AddressSetView addrSet, boolean forward) {
return refs(false) == null
? new EmptyAddressIterator()
: new IntersectionAddressSetView(
refs.getReferenceSources(Range.closed(program.snap, program.snap)), addrSet)
.getAddresses(forward);
if (refs(false) == null) {
return new EmptyAddressIterator();
}
return new IntersectionAddressSetView(addrSet, program.viewport.unionedAddresses(
s -> refs.getReferenceSources(Range.singleton(s)))).getAddresses(forward);
}
@Override
public AddressIterator getReferenceDestinationIterator(Address startAddr, boolean forward) {
return refs(false) == null
? new EmptyAddressIterator()
: refs.getReferenceDestinations(Range.closed(program.snap, program.snap))
if (refs(false) == null) {
return new EmptyAddressIterator();
}
return program.viewport.unionedAddresses(
s -> refs.getReferenceDestinations(Range.singleton(s)))
.getAddresses(startAddr, forward);
}
@Override
public AddressIterator getReferenceDestinationIterator(AddressSetView addrSet,
boolean forward) {
return refs(false) == null
? new EmptyAddressIterator()
: new IntersectionAddressSetView(
refs.getReferenceDestinations(Range.closed(program.snap, program.snap)),
addrSet).getAddresses(forward);
if (refs(false) == null) {
return new EmptyAddressIterator();
}
return new IntersectionAddressSetView(addrSet, program.viewport.unionedAddresses(
s -> refs.getReferenceDestinations(Range.singleton(s)))).getAddresses(forward);
}
@Override
public int getReferenceCountTo(Address toAddr) {
return refs(false) == null
? 0
: refs.getReferenceCountTo(program.snap, toAddr);
if (refs(false) == null) {
return 0;
}
if (!program.viewport.isForked()) {
return refs.getReferenceCountTo(program.snap, toAddr);
}
return collect(s -> refs.getReferencesTo(s, toAddr)).size();
}
@Override
public int getReferenceCountFrom(Address fromAddr) {
return refs(false) == null
? 0
: refs.getReferenceCountFrom(program.snap, fromAddr);
if (refs(false) == null) {
return 0;
}
if (!program.viewport.isForked()) {
return refs.getReferenceCountFrom(program.snap, fromAddr);
}
return collect(s -> refs.getReferencesFrom(s, fromAddr)).size();
}
@Override
public int getReferenceDestinationCount() {
// TODO: It is unclear if the interface definition means to include unique addresses
// or also unique references
return refs(false) == null
? 0
: (int) refs.getReferenceDestinations(Range.closed(program.snap, program.snap))
if (refs(false) == null) {
return 0;
}
return (int) program.viewport
.unionedAddresses(s -> refs.getReferenceDestinations(Range.singleton(s)))
.getNumAddresses();
}
@ -320,17 +355,17 @@ public abstract class AbstractDBTraceProgramViewReferenceManager implements Refe
public int getReferenceSourceCount() {
// TODO: It is unclear if the interface definition means to include unique addresses
// or also unique references
return refs(false) == null
? 0
: (int) refs.getReferenceSources(Range.closed(program.snap, program.snap))
if (refs(false) == null) {
return 0;
}
return (int) program.viewport
.unionedAddresses(s -> refs.getReferenceSources(Range.singleton(s)))
.getNumAddresses();
}
@Override
public boolean hasReferencesTo(Address toAddr) {
return refs(false) == null
? false
: !refs.getReferencesTo(program.snap, toAddr).isEmpty();
return any(false, s -> !refs.getReferencesTo(s, toAddr).isEmpty());
}
@Override
@ -361,9 +396,11 @@ public abstract class AbstractDBTraceProgramViewReferenceManager implements Refe
/**
* Get the reference level for a given reference type
*
* <p>
* TODO: Why is this not a property of {@link RefType}, or a static method of
* {@link SymbolUtilities}?
*
* <p>
* Note that this was copy-pasted from {@code BigRefListV0}, and there's an exact copy also in
* {@code RefListV0}.
*
@ -389,11 +426,13 @@ public abstract class AbstractDBTraceProgramViewReferenceManager implements Refe
/**
* {@inheritDoc}
*
* <p>
* To clarify, "reference level" is a sort of priority assigned to each reference type. See,
* e.g., {@link SymbolUtilities#SUB_LEVEL}. Each is a byte constant, and greater values imply
* higher priority. This method returns the highest priority of any reference to the given
* address.
*
* <p>
* TODO: Track this in the database?
*/
@Override
@ -402,9 +441,11 @@ public abstract class AbstractDBTraceProgramViewReferenceManager implements Refe
return SymbolUtilities.UNK_LEVEL;
}
byte highest = SymbolUtilities.UNK_LEVEL;
for (TraceReference ref : refs.getReferencesTo(program.snap, toAddr)) {
for (long s : program.viewport.getOrderedSnaps()) {
for (TraceReference ref : refs.getReferencesTo(s, toAddr)) {
highest = (byte) Math.max(highest, getRefLevel(ref.getReferenceType()));
}
}
return highest;
}
}

View file

@ -17,8 +17,10 @@ package ghidra.trace.database.program;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.*;
import java.util.Map.Entry;
import java.util.function.BiFunction;
import org.apache.commons.lang3.tuple.Pair;
@ -34,6 +36,7 @@ import ghidra.program.model.address.*;
import ghidra.program.model.data.*;
import ghidra.program.model.lang.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.pcode.Varnode;
import ghidra.program.model.reloc.RelocationTable;
import ghidra.program.model.symbol.*;
@ -42,9 +45,9 @@ import ghidra.program.model.util.PropertyMapManager;
import ghidra.program.util.ChangeManager;
import ghidra.program.util.ProgramChangeRecord;
import ghidra.trace.database.DBTrace;
import ghidra.trace.database.listing.DBTraceCodeRegisterSpace;
import ghidra.trace.database.memory.DBTraceMemoryRegion;
import ghidra.trace.database.memory.DBTraceMemoryRegisterSpace;
import ghidra.trace.database.listing.*;
import ghidra.trace.database.memory.*;
import ghidra.trace.database.symbol.DBTraceFunctionSymbolView;
import ghidra.trace.model.Trace.*;
import ghidra.trace.model.TraceAddressSnapRange;
import ghidra.trace.model.TraceDomainObjectListener;
@ -53,26 +56,29 @@ import ghidra.trace.model.bookmark.TraceBookmarkType;
import ghidra.trace.model.data.TraceBasedDataTypeManager;
import ghidra.trace.model.listing.*;
import ghidra.trace.model.memory.TraceMemoryRegion;
import ghidra.trace.model.memory.TraceMemoryState;
import ghidra.trace.model.program.TraceProgramView;
import ghidra.trace.model.program.TraceProgramViewMemory;
import ghidra.trace.model.symbol.*;
import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.util.TraceAddressSpace;
import ghidra.util.PairingIteratorMerger;
import ghidra.util.UniversalID;
import ghidra.trace.util.*;
import ghidra.trace.util.TraceTimeViewport.*;
import ghidra.util.*;
import ghidra.util.datastruct.WeakValueHashMap;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* TODO
* A wrapper on a trace, which given a snap, implements the {@link Program} interface
*
* <p>
* NOTE: Calling {@link CodeUnit#getProgram()} from units contained in this view may not necessarily
* return this same view. If the code unit comes from a less-recent snap than the snap associated
* with this view, the view for that snap is returned instead.
*
* TODO: Unit tests for all of this
* <p>
* TODO: Unit tests for all of this.
*/
public class DBTraceProgramView implements TraceProgramView {
public static final int TIME_INTERVAL = 100;
@ -160,58 +166,6 @@ public class DBTraceProgramView implements TraceProgramView {
listenFor(TraceSymbolChangeType.DELETED, this::symbolDeleted);
}
protected DomainObjectEventQueues getEventQueues(TraceAddressSpace space) {
// TODO: Should there be views on other frames?
// IIRC, this was an abandoned experiment for "register listings"
TraceThread thread = space == null ? null : space.getThread();
if (thread == null) {
return eventQueues;
}
DBTraceProgramViewRegisters viewRegisters;
synchronized (regViewsByThread) {
viewRegisters = regViewsByThread.get(thread);
}
return viewRegisters == null ? null : viewRegisters.eventQueues;
}
protected DomainObjectEventQueues isVisible(TraceAddressSpace space,
TraceAddressSnapRange range) {
if (!range.getLifespan().contains(snap)) {
return null;
}
return getEventQueues(space);
}
protected DomainObjectEventQueues isVisible(TraceAddressSpace space, TraceCodeUnit cu) {
if (!cu.getLifespan().contains(snap)) {
return null;
}
return getEventQueues(space);
}
protected DomainObjectEventQueues isVisible(TraceAddressSpace space, TraceSymbol symbol) {
DomainObjectEventQueues queues = getEventQueues(space);
if (queues == null) {
return null;
}
if (symbol instanceof TraceVariableSymbol) {
TraceVariableSymbol var = (TraceVariableSymbol) symbol;
TraceFunctionSymbol func = var.getFunction();
if (func == null) {
return queues;
}
return func.getLifespan().contains(snap) ? queues : null;
}
if (!(symbol instanceof TraceSymbolWithLifespan)) {
return queues;
}
TraceSymbolWithLifespan symWl = (TraceSymbolWithLifespan) symbol;
if (!symWl.getLifespan().contains(snap)) {
return null;
}
return queues;
}
private void eventPassthrough(DomainObjectChangeRecord rec) {
fireEventAllViews(rec);
}
@ -227,13 +181,10 @@ public class DBTraceProgramView implements TraceProgramView {
}
private void bookmarkAdded(TraceAddressSpace space, TraceBookmark bm) {
DomainObjectEventQueues queues = getEventQueues(space);
DomainObjectEventQueues queues = isBookmarkVisible(space, bm);
if (queues == null) {
return;
}
if (!bm.getLifespan().contains(snap)) {
return;
}
fireBookmarkAdded(queues, bm);
}
@ -243,13 +194,10 @@ public class DBTraceProgramView implements TraceProgramView {
}
private void bookmarkChanged(TraceAddressSpace space, TraceBookmark bm) {
DomainObjectEventQueues queues = getEventQueues(space);
DomainObjectEventQueues queues = isBookmarkVisible(space, bm);
if (queues == null) {
return;
}
if (!bm.getLifespan().contains(snap)) {
return;
}
fireBookmarkChanged(queues, bm);
}
@ -259,14 +207,13 @@ public class DBTraceProgramView implements TraceProgramView {
}
private void bookmarkLifespanChanged(TraceAddressSpace space, TraceBookmark bm,
Range<Long> oldSpan,
Range<Long> newSpan) {
Range<Long> oldSpan, Range<Long> newSpan) {
DomainObjectEventQueues queues = getEventQueues(space);
if (queues == null) {
return;
}
boolean inOld = oldSpan.contains(snap);
boolean inNew = newSpan.contains(snap);
boolean inOld = isBookmarkVisible(bm, oldSpan);
boolean inNew = isBookmarkVisible(bm, newSpan);
if (inOld && !inNew) {
fireBookmarkRemoved(queues, bm);
}
@ -276,13 +223,10 @@ public class DBTraceProgramView implements TraceProgramView {
}
private void bookmarkDeleted(TraceAddressSpace space, TraceBookmark bm) {
DomainObjectEventQueues queues = getEventQueues(space);
DomainObjectEventQueues queues = isBookmarkVisible(space, bm);
if (queues == null) {
return;
}
if (!bm.getLifespan().contains(snap)) {
return;
}
fireBookmarkRemoved(queues, bm);
}
@ -317,7 +261,9 @@ public class DBTraceProgramView implements TraceProgramView {
private void codeAdded(TraceAddressSpace space, TraceAddressSnapRange range,
TraceCodeUnit oldIsNull, TraceCodeUnit added) {
// NOTE: Added code may be coalesced range. -added- is just first unit.
DomainObjectEventQueues queues = isVisible(space, range);
// TODO: The range may contain many units, so this could be broken down
DomainObjectEventQueues queues =
isCodeVisible(space, range) ? getEventQueues(space) : null;
if (queues == null) {
return;
}
@ -335,8 +281,8 @@ public class DBTraceProgramView implements TraceProgramView {
if (queues == null) {
return;
}
boolean inOld = oldSpan.contains(snap);
boolean inNew = newSpan.contains(snap);
boolean inOld = isCodeVisible(unit, oldSpan);
boolean inNew = isCodeVisible(unit, newSpan);
if (inOld && !inNew) {
fireCodeRemoved(queues, unit.getMinAddress(), unit.getMaxAddress(), unit);
}
@ -348,7 +294,7 @@ public class DBTraceProgramView implements TraceProgramView {
private void codeRemoved(TraceAddressSpace space, TraceAddressSnapRange range,
TraceCodeUnit removed, TraceCodeUnit newIsNull) {
// NOTE: Removed code may be coalesced range. -removed- is just first unit.
DomainObjectEventQueues queues = isVisible(space, range);
DomainObjectEventQueues queues = isCodeVisible(space, removed);
if (queues == null) {
return;
}
@ -373,6 +319,7 @@ public class DBTraceProgramView implements TraceProgramView {
private void codeDataTypeReplaced(TraceAddressSpace space, TraceAddressSnapRange range,
Long oldDataTypeID, Long newDataTypeID) {
// TODO??: "code" visibility check may not be necessary or advantageous
DomainObjectEventQueues queues = isVisible(space, range);
if (queues == null) {
return;
@ -383,7 +330,7 @@ public class DBTraceProgramView implements TraceProgramView {
private void compositeDataAdded(TraceAddressSpace space, TraceAddressSnapRange range,
TraceData oldIsNull, TraceData added) {
DomainObjectEventQueues queues = isVisible(space, range);
DomainObjectEventQueues queues = isCodeVisible(space, added);
if (queues == null) {
return;
}
@ -397,8 +344,8 @@ public class DBTraceProgramView implements TraceProgramView {
if (queues == null) {
return;
}
boolean inOld = oldSpan.contains(snap);
boolean inNew = newSpan.contains(snap);
boolean inOld = isCodeVisible(data, oldSpan);
boolean inNew = isCodeVisible(data, newSpan);
if (inOld && !inNew) {
queues.fireEvent(new ProgramChangeRecord(ChangeManager.DOCR_COMPOSITE_REMOVED,
data.getMinAddress(), data.getMaxAddress(), null, data, null));
@ -411,7 +358,7 @@ public class DBTraceProgramView implements TraceProgramView {
private void compositeDataRemoved(TraceAddressSpace space, TraceAddressSnapRange range,
TraceData removed, TraceData newIsNull) {
DomainObjectEventQueues queues = isVisible(space, range);
DomainObjectEventQueues queues = isCodeVisible(space, removed);
if (queues == null) {
return;
}
@ -473,7 +420,7 @@ public class DBTraceProgramView implements TraceProgramView {
private void functionChangedGeneric(TraceAddressSpace space, TraceFunctionSymbol function,
int type, int subType) {
DomainObjectEventQueues queues = isVisible(space, function);
DomainObjectEventQueues queues = isFunctionVisible(space, function);
if (queues == null) {
return;
}
@ -557,7 +504,7 @@ public class DBTraceProgramView implements TraceProgramView {
private void instructionFlowOverrideChanged(TraceAddressSpace space,
TraceInstruction instruction, FlowOverride oldOverride, FlowOverride newOverride) {
DomainObjectEventQueues queues = isVisible(space, instruction);
DomainObjectEventQueues queues = isCodeVisible(space, instruction);
if (queues == null) {
return;
}
@ -567,7 +514,7 @@ public class DBTraceProgramView implements TraceProgramView {
private void instructionFallThroughChanged(TraceAddressSpace space,
TraceInstruction instruction, boolean oldFallThrough, boolean newFallThrough) {
DomainObjectEventQueues queues = isVisible(space, instruction);
DomainObjectEventQueues queues = isCodeVisible(space, instruction);
if (queues == null) {
return;
}
@ -577,7 +524,7 @@ public class DBTraceProgramView implements TraceProgramView {
private void memoryBytesChanged(TraceAddressSpace space, TraceAddressSnapRange range,
byte[] oldIsNull, byte[] bytes) {
DomainObjectEventQueues queues = isVisible(space, range);
DomainObjectEventQueues queues = isBytesVisible(space, range);
if (queues == null) {
return;
}
@ -591,7 +538,7 @@ public class DBTraceProgramView implements TraceProgramView {
}
private void memoryRegionAdded(TraceAddressSpace space, TraceMemoryRegion region) {
if (!region.getLifespan().contains(snap)) {
if (!isRegionVisible(region)) {
return;
}
// NOTE: Register view regions are fixed
@ -602,7 +549,7 @@ public class DBTraceProgramView implements TraceProgramView {
}
private void memoryRegionChanged(TraceAddressSpace space, TraceMemoryRegion region) {
if (!region.getLifespan().contains(snap)) {
if (!isRegionVisible(region)) {
return;
}
eventQueues.fireEvent(new ProgramChangeRecord(ChangeManager.DOCR_MEMORY_BLOCK_CHANGED,
@ -614,8 +561,8 @@ public class DBTraceProgramView implements TraceProgramView {
private void memoryRegionLifespanChanged(TraceAddressSpace space, TraceMemoryRegion region,
Range<Long> oldSpan, Range<Long> newSpan) {
boolean inOld = oldSpan.contains(snap);
boolean inNew = newSpan.contains(snap);
boolean inOld = isRegionVisible(region, oldSpan);
boolean inNew = isRegionVisible(region, newSpan);
if (inOld && !inNew) {
eventQueues.fireEvent(
new ProgramChangeRecord(ChangeManager.DOCR_MEMORY_BLOCK_REMOVED,
@ -637,7 +584,7 @@ public class DBTraceProgramView implements TraceProgramView {
// HACK
listing.fragmentsByRegion.remove(region);
// END HACK
if (!region.getLifespan().contains(snap)) {
if (!isRegionVisible(region)) {
return;
}
eventQueues.fireEvent(new ProgramChangeRecord(ChangeManager.DOCR_MEMORY_BLOCK_REMOVED,
@ -678,7 +625,7 @@ public class DBTraceProgramView implements TraceProgramView {
}
private void symbolAdded(TraceAddressSpace space, TraceSymbol symbol) {
DomainObjectEventQueues queues = isVisible(space, symbol);
DomainObjectEventQueues queues = isSymbolVisible(space, symbol);
if (queues == null) {
return;
}
@ -697,7 +644,7 @@ public class DBTraceProgramView implements TraceProgramView {
}
private void symbolSourceChanged(TraceAddressSpace space, TraceSymbol symbol) {
DomainObjectEventQueues queues = isVisible(space, symbol);
DomainObjectEventQueues queues = isSymbolVisible(space, symbol);
if (queues == null) {
return;
}
@ -709,11 +656,11 @@ public class DBTraceProgramView implements TraceProgramView {
private void symbolSetAsPrimary(TraceAddressSpace space, TraceSymbol symbol,
TraceSymbol oldPrimary, TraceSymbol newPrimary) {
// NOTE symbol == newPrimary
DomainObjectEventQueues newQueues = isVisible(space, symbol);
DomainObjectEventQueues newQueues = isSymbolVisible(space, symbol);
if (newQueues == null) {
return;
}
DomainObjectEventQueues oldQueues = isVisible(space, oldPrimary);
DomainObjectEventQueues oldQueues = isSymbolVisible(space, oldPrimary);
if (oldPrimary != null && oldQueues == null) {
oldPrimary = null;
}
@ -725,7 +672,7 @@ public class DBTraceProgramView implements TraceProgramView {
private void symbolRenamed(TraceAddressSpace space, TraceSymbol symbol, String oldName,
String newName) {
DomainObjectEventQueues queues = isVisible(space, symbol);
DomainObjectEventQueues queues = isSymbolVisible(space, symbol);
if (queues == null) {
return;
}
@ -736,7 +683,7 @@ public class DBTraceProgramView implements TraceProgramView {
private void symbolParentChanged(TraceAddressSpace space, TraceSymbol symbol,
TraceNamespaceSymbol oldParent, TraceNamespaceSymbol newParent) {
DomainObjectEventQueues queues = isVisible(space, symbol);
DomainObjectEventQueues queues = isSymbolVisible(space, symbol);
if (queues == null) {
return;
}
@ -747,7 +694,7 @@ public class DBTraceProgramView implements TraceProgramView {
private void symbolAssociationAdded(TraceAddressSpace space, TraceSymbol symbol,
TraceReference oldRefIsNull, TraceReference newRef) {
DomainObjectEventQueues queues = isVisible(space, symbol);
DomainObjectEventQueues queues = isSymbolVisible(space, symbol);
if (queues == null) {
return;
}
@ -759,7 +706,7 @@ public class DBTraceProgramView implements TraceProgramView {
private void symbolAssociationRemoved(TraceAddressSpace space, TraceSymbol symbol,
TraceReference oldRef, TraceReference newRefIsNull) {
DomainObjectEventQueues queues = isVisible(space, symbol);
DomainObjectEventQueues queues = isSymbolVisible(space, symbol);
if (queues == null) {
return;
}
@ -770,7 +717,7 @@ public class DBTraceProgramView implements TraceProgramView {
private void symbolAddressChanged(TraceAddressSpace space, TraceSymbol symbol,
Address oldAddress, Address newAddress) {
DomainObjectEventQueues queues = isVisible(space, symbol);
DomainObjectEventQueues queues = isSymbolVisible(space, symbol);
if (queues == null) {
return;
}
@ -779,14 +726,14 @@ public class DBTraceProgramView implements TraceProgramView {
checkVariableFunctionChanged(space, symbol);
}
private void symbolLifespanChanged(TraceAddressSpace space, TraceSymbol symbol,
private void symbolLifespanChanged(TraceAddressSpace space, TraceSymbolWithLifespan symbol,
Range<Long> oldSpan, Range<Long> newSpan) {
DomainObjectEventQueues queues = getEventQueues(space);
if (queues == null) {
return;
}
boolean inOld = oldSpan.contains(snap);
boolean inNew = newSpan.contains(snap);
boolean inOld = isSymbolWithLifespanVisible(symbol, oldSpan);
boolean inNew = isSymbolWithLifespanVisible(symbol, newSpan);
if (inOld && !inNew) {
fireSymbolRemoved(queues, symbol);
if (symbol instanceof TraceFunctionSymbol) {
@ -807,7 +754,7 @@ public class DBTraceProgramView implements TraceProgramView {
}
private void symbolDeleted(TraceAddressSpace space, TraceSymbol symbol) {
DomainObjectEventQueues queues = isVisible(space, symbol);
DomainObjectEventQueues queues = isSymbolVisible(space, symbol);
if (queues == null) {
return;
}
@ -830,6 +777,7 @@ public class DBTraceProgramView implements TraceProgramView {
protected static class OverlappingAddressRangeKeyIteratorMerger<T> extends
PairingIteratorMerger<Entry<AddressRange, T>, Entry<AddressRange, T>, Entry<AddressRange, T>> {
protected static <T> Iterable<Pair<Entry<AddressRange, T>, Entry<AddressRange, T>>> iter(
Iterable<Entry<AddressRange, T>> left, Iterable<Entry<AddressRange, T>> right) {
return new Iterable<>() {
@ -864,6 +812,7 @@ public class DBTraceProgramView implements TraceProgramView {
protected final DomainObjectEventQueues eventQueues;
protected EventTranslator eventTranslator;
protected final AddressSet allAddresses = new AddressSet();
protected final DBTraceProgramViewBookmarkManager bookmarkManager;
protected final DBTraceProgramViewEquateTable equateTable;
@ -881,17 +830,28 @@ public class DBTraceProgramView implements TraceProgramView {
protected final Map<TraceThread, DBTraceProgramViewRegisters> regViewsByThread;
protected long snap;
protected final DefaultTraceTimeViewport viewport;
protected final Runnable viewportChangeListener = this::viewportChanged;
// This is a strange thing
Long versionTag = 0L;
public DBTraceProgramView(DBTrace trace, long snap, CompilerSpec compilerSpec) {
for (AddressSpace space : trace.getBaseAddressFactory().getPhysicalSpaces()) {
if (space.getType() == AddressSpace.TYPE_OTHER) {
continue;
}
allAddresses.add(space.getMinAddress(), space.getMaxAddress());
}
this.trace = trace;
this.snap = snap;
this.languageID = compilerSpec.getLanguage().getLanguageID();
this.language = compilerSpec.getLanguage();
this.compilerSpec = compilerSpec;
this.viewport = new DefaultTraceTimeViewport(trace);
this.viewport.setSnap(snap);
this.eventQueues =
new DomainObjectEventQueues(this, TIME_INTERVAL, BUF_SIZE, trace.getLock());
@ -911,6 +871,10 @@ public class DBTraceProgramView implements TraceProgramView {
}
protected void viewportChanged() {
eventQueues.fireEvent(new DomainObjectChangeRecord(DomainObject.DO_OBJECT_RESTORED));
}
protected void fireEventAllViews(DomainObjectChangeRecord ev) {
// TODO: Do I need to make copies?
eventQueues.fireEvent(ev);
@ -934,6 +898,11 @@ public class DBTraceProgramView implements TraceProgramView {
return snap;
}
@Override
public TraceTimeViewport getViewport() {
return viewport;
}
@Override
public Long getMaxSnap() {
return trace.getTimeManager().getMaxSnap();
@ -1527,21 +1496,21 @@ public class DBTraceProgramView implements TraceProgramView {
}
public void updateMemoryAddBlock(DBTraceMemoryRegion region) {
if (!region.getLifespan().contains(snap)) {
if (!isRegionVisible(region)) {
return;
}
memory.updateAddBlock(region);
}
public void updateMemoryChangeBlockName(DBTraceMemoryRegion region) {
if (!region.getLifespan().contains(snap)) {
if (!isRegionVisible(region)) {
return;
}
memory.updateChangeBlockName(region);
}
public void updateMemoryChangeBlockFlags(DBTraceMemoryRegion region) {
if (!region.getLifespan().contains(snap)) {
if (!isRegionVisible(region)) {
return;
}
memory.updateChangeBlockFlags(region);
@ -1549,7 +1518,7 @@ public class DBTraceProgramView implements TraceProgramView {
public void updateMemoryChangeBlockRange(DBTraceMemoryRegion region, AddressRange oldRange,
AddressRange newRange) {
if (!region.getLifespan().contains(snap)) {
if (!isRegionVisible(region)) {
return;
}
memory.updateChangeBlockRange(region, oldRange, newRange);
@ -1557,8 +1526,8 @@ public class DBTraceProgramView implements TraceProgramView {
public void updateMemoryChangeBlockLifespan(DBTraceMemoryRegion region,
Range<Long> oldLifespan, Range<Long> newLifespan) {
boolean inOld = oldLifespan.contains(snap);
boolean inNew = newLifespan.contains(snap);
boolean inOld = isRegionVisible(region, oldLifespan);
boolean inNew = isRegionVisible(region, newLifespan);
if (inOld && !inNew) {
memory.updateDeleteBlock(region);
}
@ -1568,7 +1537,7 @@ public class DBTraceProgramView implements TraceProgramView {
}
public void updateMemoryDeleteBlock(DBTraceMemoryRegion region) {
if (!region.getLifespan().contains(snap)) {
if (!isRegionVisible(region)) {
return;
}
memory.updateAddBlock(region);
@ -1577,4 +1546,234 @@ public class DBTraceProgramView implements TraceProgramView {
public void updateMemoryRefreshBlocks() {
memory.updateRefreshBlocks();
}
protected DomainObjectEventQueues getEventQueues(TraceAddressSpace space) {
// TODO: Should there be views on other frames?
// IIRC, this was an abandoned experiment for "register listings"
TraceThread thread = space == null ? null : space.getThread();
if (thread == null) {
return eventQueues;
}
DBTraceProgramViewRegisters viewRegisters;
synchronized (regViewsByThread) {
viewRegisters = regViewsByThread.get(thread);
}
return viewRegisters == null ? null : viewRegisters.eventQueues;
}
protected DomainObjectEventQueues isVisible(TraceAddressSpace space,
TraceAddressSnapRange range) {
return viewport.containsAnyUpper(range.getLifespan()) ? getEventQueues(space) : null;
}
protected boolean isBookmarkVisible(TraceBookmark bm, Range<Long> lifespan) {
return viewport.containsAnyUpper(lifespan);
}
protected DomainObjectEventQueues isBookmarkVisible(TraceAddressSpace space, TraceBookmark bm) {
return isBookmarkVisible(bm, bm.getLifespan()) ? getEventQueues(space) : null;
}
protected boolean bytesDifferForSet(byte[] b1, byte[] b2, AddressSetView set) {
Address min = set.getMinAddress();
for (AddressRange rng : set) {
int beg = (int) rng.getMinAddress().subtract(min);
int end = beg + (int) rng.getLength();
if (!Arrays.equals(b1, beg, end, b2, beg, end)) {
return true;
}
}
return false;
}
protected Occlusion<TraceCodeUnit> getCodeOcclusion(TraceAddressSpace space) {
return new RangeQueryOcclusion<>() {
final DBTraceCodeSpace codeSpace = trace.getCodeManager().get(space, false);
final DBTraceMemorySpace memSpace = trace.getMemoryManager().get(space, false);
final DBTraceDefinedUnitsView definedUnits =
codeSpace == null ? null : codeSpace.definedUnits();
public boolean occluded(TraceCodeUnit cu, AddressRange range, Range<Long> span) {
if (cu == null) {
return RangeQueryOcclusion.super.occluded(cu, range, span);
}
AddressSetView known =
memSpace.getAddressesWithState(span, s -> s == TraceMemoryState.KNOWN);
if (!known.intersects(range.getMinAddress(), range.getMaxAddress())) {
return RangeQueryOcclusion.super.occluded(cu, range, span);
}
byte[] memBytes = new byte[cu.getLength()];
memSpace.getBytes(span.upperEndpoint(), cu.getMinAddress(),
ByteBuffer.wrap(memBytes));
byte[] cuBytes;
try {
cuBytes = cu.getBytes();
}
catch (MemoryAccessException e) {
throw new AssertionError(e);
}
AddressSetView intersectKnown =
new IntersectionAddressSetView(new AddressSet(range), known);
if (bytesDifferForSet(memBytes, cuBytes, intersectKnown)) {
return true;
}
return RangeQueryOcclusion.super.occluded(cu, range, span);
}
@Override
public Iterable<? extends TraceCodeUnit> query(AddressRange range, Range<Long> span) {
return definedUnits == null
? Collections.emptyList()
: definedUnits.get(span.upperEndpoint(), range, true);
}
@Override
public AddressRange range(TraceCodeUnit cu) {
return cu.getRange();
}
};
}
protected <T extends TraceCodeUnit> T getTopCode(Address address,
BiFunction<TraceCodeSpace, Long, T> codeFunc) {
DBTraceCodeSpace codeSpace =
trace.getCodeManager().getCodeSpace(address.getAddressSpace(), false);
if (codeSpace == null) {
return null;
}
return viewport.getTop(s -> {
T t = codeFunc.apply(codeSpace, s);
if (t != null && isCodeVisible(t, t.getLifespan())) {
return t;
}
return null;
});
}
protected boolean isCodeVisible(TraceCodeUnit cu, Range<Long> lifespan) {
return viewport.isCompletelyVisible(cu.getRange(), lifespan, cu,
getCodeOcclusion(cu.getTraceSpace()));
}
protected boolean isCodeVisible(TraceAddressSpace space, TraceAddressSnapRange range) {
return viewport.isCompletelyVisible(range.getRange(), range.getLifespan(), null,
getCodeOcclusion(space));
}
protected DomainObjectEventQueues isCodeVisible(TraceAddressSpace space, TraceCodeUnit cu) {
if (!isCodeVisible(cu, cu.getLifespan())) {
return null;
}
return getEventQueues(space);
}
protected Occlusion<TraceFunctionSymbol> getFunctionOcclusion(TraceFunctionSymbol func) {
return new QueryOcclusion<>() {
DBTraceFunctionSymbolView functions = trace.getSymbolManager().functions();
AddressSetView body = func.getBody();
@Override
public Iterable<? extends TraceFunctionSymbol> query(AddressRange range,
Range<Long> span) {
// NB. No functions in register space!
return functions.getIntersecting(Range.singleton(span.upperEndpoint()), null, range,
false);
}
public boolean itemOccludes(AddressRange range, TraceFunctionSymbol f) {
return body.intersects(f.getBody());
}
@Override
public void removeItem(AddressSet remains, TraceFunctionSymbol t) {
remains.delete(t.getBody());
}
};
}
protected boolean isFunctionVisible(TraceFunctionSymbol function, Range<Long> lifespan) {
AddressSetView body = function.getBody();
AddressRange bodySpan =
new AddressRangeImpl(body.getMinAddress(), body.getMaxAddress());
return viewport.isCompletelyVisible(bodySpan, function.getLifespan(), function,
getFunctionOcclusion(function));
}
protected DomainObjectEventQueues isFunctionVisible(TraceAddressSpace space,
TraceFunctionSymbol function) {
DomainObjectEventQueues queues = getEventQueues(space);
if (queues == null) {
return null;
}
return isFunctionVisible(function, function.getLifespan()) ? queues : null;
}
protected boolean isSymbolWithLifespanVisible(TraceSymbolWithLifespan symbol,
Range<Long> lifespan) {
if (symbol instanceof TraceFunctionSymbol) {
TraceFunctionSymbol func = (TraceFunctionSymbol) symbol;
return isFunctionVisible(func, lifespan);
}
if (!viewport.containsAnyUpper(lifespan)) {
return false;
}
return true;
}
protected DomainObjectEventQueues isSymbolVisible(TraceAddressSpace space,
TraceSymbol symbol) {
// NB. Most symbols do not occlude each other
DomainObjectEventQueues queues = getEventQueues(space);
if (queues == null) {
return null;
}
if (symbol instanceof TraceVariableSymbol) {
TraceVariableSymbol var = (TraceVariableSymbol) symbol;
TraceFunctionSymbol func = var.getFunction();
if (func == null) {
return queues;
}
return isFunctionVisible(space, func);
}
if (!(symbol instanceof TraceSymbolWithLifespan)) {
return queues;
}
TraceSymbolWithLifespan symWl = (TraceSymbolWithLifespan) symbol;
return isSymbolWithLifespanVisible(symWl, symWl.getLifespan()) ? queues : null;
}
protected DomainObjectEventQueues isBytesVisible(TraceAddressSpace space,
TraceAddressSnapRange range) {
// NB. This need not be precise....
DomainObjectEventQueues queues = getEventQueues(space);
if (queues == null) {
return null;
}
if (!viewport.containsAnyUpper(range.getLifespan())) {
return null;
}
return queues;
}
protected Occlusion<TraceMemoryRegion> regionOcclusion = new RangeQueryOcclusion<>() {
@Override
public Iterable<? extends TraceMemoryRegion> query(AddressRange range, Range<Long> span) {
return trace.getMemoryManager()
.getRegionsIntersecting(Range.singleton(span.upperEndpoint()), range);
}
@Override
public AddressRange range(TraceMemoryRegion r) {
return r.getRange();
}
};
protected boolean isRegionVisible(TraceMemoryRegion reg) {
return isRegionVisible(reg, reg.getLifespan());
}
protected boolean isRegionVisible(TraceMemoryRegion reg, Range<Long> lifespan) {
return viewport.isCompletelyVisible(reg.getRange(), lifespan, reg,
regionOcclusion);
}
}

View file

@ -23,6 +23,7 @@ import javax.swing.ImageIcon;
import org.apache.commons.collections4.IteratorUtils;
import com.google.common.collect.Iterators;
import com.google.common.collect.Range;
import generic.NestedIterator;
@ -96,7 +97,8 @@ public class DBTraceProgramViewBookmarkManager implements TraceProgramViewBookma
if (space == null) {
return null;
}
for (TraceBookmark bm : space.getBookmarksAt(program.snap, addr)) {
for (long s : program.viewport.getOrderedSnaps()) {
for (TraceBookmark bm : space.getBookmarksAt(s, addr)) {
if (!type.equals(bm.getTypeString())) {
continue;
}
@ -105,6 +107,7 @@ public class DBTraceProgramViewBookmarkManager implements TraceProgramViewBookma
}
return bm;
}
}
return null;
}
}
@ -222,12 +225,14 @@ public class DBTraceProgramViewBookmarkManager implements TraceProgramViewBookma
return EMPTY_BOOKMARK_ARRAY;
}
List<Bookmark> list = new ArrayList<>();
for (TraceBookmark bm : space.getBookmarksAt(program.snap, addr)) {
for (long s : program.viewport.getOrderedSnaps()) {
for (TraceBookmark bm : space.getBookmarksAt(s, addr)) {
if (!bm.getLifespan().contains(program.snap)) {
continue;
}
list.add(bm);
}
}
return list.toArray(new Bookmark[list.size()]);
}
}
@ -241,12 +246,14 @@ public class DBTraceProgramViewBookmarkManager implements TraceProgramViewBookma
return EMPTY_BOOKMARK_ARRAY;
}
List<Bookmark> list = new ArrayList<>();
for (TraceBookmark bm : space.getBookmarksAt(program.snap, address)) {
for (long s : program.viewport.getOrderedSnaps()) {
for (TraceBookmark bm : space.getBookmarksAt(s, address)) {
if (!type.equals(bm.getTypeString())) {
continue;
}
list.add(bm);
}
}
return list.toArray(new Bookmark[list.size()]);
}
}
@ -261,10 +268,10 @@ public class DBTraceProgramViewBookmarkManager implements TraceProgramViewBookma
return result;
}
for (TraceBookmark bm : bmt.getBookmarks()) {
if (bm.getAddress().isRegisterAddress()) {
if (bm.getAddress().getAddressSpace().isRegisterSpace()) {
continue;
}
if (!bm.getLifespan().contains(program.snap)) {
if (!program.viewport.containsAnyUpper(bm.getLifespan())) {
continue;
}
result.add(bm.getAddress());
@ -287,7 +294,7 @@ public class DBTraceProgramViewBookmarkManager implements TraceProgramViewBookma
@SuppressWarnings("unchecked")
protected static <T, U extends T> Iterator<T> filteredIterator(Iterator<U> it,
Predicate<? super U> predicate) {
return IteratorUtils.filteredIterator(it, e -> predicate.test((U) e));
return (Iterator<T>) Iterators.filter(it, e -> predicate.test(e));
}
@Override
@ -298,14 +305,22 @@ public class DBTraceProgramViewBookmarkManager implements TraceProgramViewBookma
}
// TODO: May want to offer memory-only and/or register-only bookmark iterators
return filteredIterator(bmt.getBookmarks().iterator(),
bm -> !bm.getAddress().isRegisterAddress() && bm.getLifespan().contains(program.snap));
bm -> !bm.getAddress().getAddressSpace().isRegisterSpace() &&
program.viewport.containsAnyUpper(bm.getLifespan()));
}
@Override
public Iterator<Bookmark> getBookmarksIterator() {
// TODO: This seems terribly inefficient. We'll have to see how/when it's used.
return NestedIterator.start(bookmarkManager.getActiveMemorySpaces().iterator(),
space -> filteredIterator(space.getAllBookmarks().iterator(),
bm -> bm.getLifespan().contains(program.snap)));
bm -> program.viewport.containsAnyUpper(bm.getLifespan())));
}
protected Comparator<Bookmark> getBookmarkComparator(boolean forward) {
return forward
? (b1, b2) -> b1.getAddress().compareTo(b2.getAddress())
: (b1, b2) -> -b1.getAddress().compareTo(b2.getAddress());
}
@Override
@ -320,8 +335,9 @@ public class DBTraceProgramViewBookmarkManager implements TraceProgramViewBookma
if (space == null) {
return Collections.emptyIterator();
}
return space.getBookmarksIntersecting(Range.closed(program.snap, program.snap),
rng).iterator();
return program.viewport.mergedIterator(
s -> space.getBookmarksIntersecting(Range.closed(s, s), rng).iterator(),
getBookmarkComparator(forward));
});
}

View file

@ -27,6 +27,7 @@ import ghidra.program.model.symbol.Equate;
import ghidra.program.model.symbol.EquateTable;
import ghidra.trace.database.symbol.DBTraceEquate;
import ghidra.trace.database.symbol.DBTraceEquateManager;
import ghidra.trace.model.listing.TraceCodeUnit;
import ghidra.util.IntersectionAddressSetView;
import ghidra.util.LockHold;
import ghidra.util.exception.*;
@ -97,8 +98,13 @@ public class DBTraceProgramViewEquateTable implements EquateTable {
@Override
public Equate getEquate(Address reference, int opndPosition, long value) {
try (LockHold hold = program.trace.lockRead()) {
return doGetViewEquate(
equateManager.getReferencedByValue(program.snap, reference, opndPosition, value));
TraceCodeUnit cu = program.getTopCode(reference,
(space, s) -> space.definedUnits().getContaining(s, reference));
if (cu == null) {
return null;
}
return doGetViewEquate(equateManager.getReferencedByValue(cu.getStartSnap(), reference,
opndPosition, value));
}
}
@ -106,7 +112,12 @@ public class DBTraceProgramViewEquateTable implements EquateTable {
public List<Equate> getEquates(Address reference, int opndPosition) {
try (LockHold hold = program.trace.lockRead()) {
List<Equate> result = new ArrayList<>();
for (DBTraceEquate equate : equateManager.getReferenced(program.snap, reference,
TraceCodeUnit cu = program.getTopCode(reference,
(space, s) -> space.definedUnits().getContaining(s, reference));
if (cu == null) {
return result;
}
for (DBTraceEquate equate : equateManager.getReferenced(cu.getStartSnap(), reference,
opndPosition)) {
result.add(doGetViewEquate(equate));
}
@ -118,7 +129,12 @@ public class DBTraceProgramViewEquateTable implements EquateTable {
public List<Equate> getEquates(Address reference) {
try (LockHold hold = program.trace.lockRead()) {
List<Equate> result = new ArrayList<>();
for (DBTraceEquate equate : equateManager.getReferenced(program.snap, reference)) {
TraceCodeUnit cu = program.getTopCode(reference,
(space, s) -> space.definedUnits().getContaining(s, reference));
if (cu == null) {
return result;
}
for (DBTraceEquate equate : equateManager.getReferenced(cu.getStartSnap(), reference)) {
result.add(doGetViewEquate(equate));
}
return result;
@ -127,9 +143,9 @@ public class DBTraceProgramViewEquateTable implements EquateTable {
@Override
public AddressIterator getEquateAddresses() {
return equateManager.getReferringAddresses(Range.singleton(program.snap))
.getAddresses(
true);
return program.viewport
.unionedAddresses(s -> equateManager.getReferringAddresses(Range.singleton(s)))
.getAddresses(true);
}
@Override
@ -150,14 +166,15 @@ public class DBTraceProgramViewEquateTable implements EquateTable {
@Override
public AddressIterator getEquateAddresses(Address start) {
return equateManager.getReferringAddresses(Range.singleton(program.snap))
.getAddresses(
start, true);
return program.viewport
.unionedAddresses(s -> equateManager.getReferringAddresses(Range.singleton(s)))
.getAddresses(start, true);
}
@Override
public AddressIterator getEquateAddresses(AddressSetView asv) {
return new IntersectionAddressSetView(asv,
equateManager.getReferringAddresses(Range.singleton(program.snap))).getAddresses(true);
return new IntersectionAddressSetView(asv, program.viewport
.unionedAddresses(s -> equateManager.getReferringAddresses(Range.singleton(s))))
.getAddresses(true);
}
}

View file

@ -21,8 +21,6 @@ import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterators;
import com.google.common.collect.Range;
import generic.NestedIterator;
@ -35,8 +33,10 @@ import ghidra.program.model.symbol.Namespace;
import ghidra.program.model.symbol.SourceType;
import ghidra.trace.database.DBTraceUtils;
import ghidra.trace.database.symbol.*;
import ghidra.trace.model.listing.TraceData;
import ghidra.trace.model.symbol.TraceFunctionSymbol;
import ghidra.trace.util.EmptyFunctionIterator;
import ghidra.trace.util.WrappingFunctionIterator;
import ghidra.util.LockHold;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.InvalidInputException;
@ -44,34 +44,6 @@ import ghidra.util.task.TaskMonitor;
public class DBTraceProgramViewFunctionManager implements FunctionManager {
public static class FunctionIteratorAdapter implements FunctionIterator {
private Iterator<? extends Function> it;
public FunctionIteratorAdapter(Iterator<? extends Function> it) {
this.it = it;
}
public <T extends Function> FunctionIteratorAdapter(Iterator<T> it,
Predicate<? super T> filter) {
this.it = Iterators.filter(it, filter);
}
@Override
public boolean hasNext() {
return it.hasNext();
}
@Override
public Function next() {
return it.next();
}
@Override
public Iterator<Function> iterator() {
return this;
}
}
protected final DBTraceProgramView program;
protected final DBTraceFunctionSymbolView functions;
protected final DBTraceNamespaceSymbol global;
@ -176,27 +148,37 @@ public class DBTraceProgramViewFunctionManager implements FunctionManager {
if (!entryPoint.getAddressSpace().isMemorySpace()) {
return null;
}
for (long s : program.viewport.getOrderedSnaps()) {
// NOTE: There ought only to be one, since no overlaps allowed.
for (TraceFunctionSymbol at : functions.getAt(program.snap, null, entryPoint, false)) {
for (TraceFunctionSymbol at : functions.getAt(s, null, entryPoint, false)) {
if (entryPoint.equals(at.getEntryPoint())) {
return at;
}
else {
return null; // Anything below is occluded by the found function
}
}
}
return null;
}
@Override
public TraceFunctionSymbol getReferencedFunction(Address address) {
if (!address.getAddressSpace().isMemorySpace()) {
return null;
}
TraceFunctionSymbol found = getFunctionAt(address);
if (found != null) {
return found;
}
// We're assuming a data reference
if (program.trace.getCodeManager().data().getContaining(program.snap, address) == null) {
TraceData data =
program.getTopCode(address, (space, s) -> space.data().getContaining(s, address));
if (data == null) {
return null;
}
DBTraceReference ref =
program.trace.getReferenceManager().getPrimaryReferenceFrom(program.snap, address, 0);
DBTraceReference ref = program.trace.getReferenceManager()
.getPrimaryReferenceFrom(data.getStartSnap(), address, 0);
return ref == null ? null : getFunctionAt(ref.getToAddress());
}
@ -228,7 +210,7 @@ public class DBTraceProgramViewFunctionManager implements FunctionManager {
@Override
public FunctionIterator getFunctions(AddressSetView asv, boolean forward) {
return new FunctionIteratorAdapter(
return new WrappingFunctionIterator(
NestedIterator.start(asv.iterator(forward), rng -> getFunctionsInRange(rng, forward)),
f -> {
if (!asv.contains(f.getEntryPoint())) {
@ -251,7 +233,7 @@ public class DBTraceProgramViewFunctionManager implements FunctionManager {
@Override
public FunctionIterator getFunctionsNoStubs(AddressSetView asv, boolean forward) {
return new FunctionIteratorAdapter(
return new WrappingFunctionIterator(
NestedIterator.start(asv.iterator(forward), rng -> getFunctionsInRange(rng, forward)),
f -> {
if (f.isThunk()) {
@ -316,7 +298,7 @@ public class DBTraceProgramViewFunctionManager implements FunctionManager {
@Override
public Iterator<Function> getFunctionsOverlapping(AddressSetView set) {
return new FunctionIteratorAdapter(
return new WrappingFunctionIterator(
NestedIterator.start(set.iterator(true), rng -> getFunctionsInRange(rng, true)));
}

View file

@ -22,8 +22,6 @@ import ghidra.util.LockHold;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
// TODO: Make at/since configurable?
// NOTE: Probably not, esp., if I get the coloring right.
public class DBTraceProgramViewListing extends AbstractDBTraceProgramViewListing {
protected final AddressSet allMemory;
@ -36,12 +34,12 @@ public class DBTraceProgramViewListing extends AbstractDBTraceProgramViewListing
public boolean isUndefined(Address start, Address end) {
try (LockHold hold = program.trace.lockRead()) {
for (AddressRange range : program.getAddressFactory().getAddressSet(start, end)) {
if (!codeOperations.undefinedData()
.coversRange(
Range.closed(program.snap, program.snap), range)) {
for (long s : program.viewport.getOrderedSnaps()) {
if (!isUndefinedRange(s, range)) {
return false;
}
}
}
return true;
}
}

View file

@ -16,6 +16,8 @@
package ghidra.trace.database.program;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import com.google.common.cache.CacheBuilder;
@ -32,13 +34,33 @@ public class DBTraceProgramViewMemory extends AbstractDBTraceProgramViewMemory {
super(program);
}
protected DBTraceMemoryRegion getTopRegion(Function<Long, DBTraceMemoryRegion> regFunc) {
return program.viewport.getTop(s -> {
// TODO: There is probably an early-bail condition I can check for.
DBTraceMemoryRegion reg = regFunc.apply(s);
if (reg != null && program.isRegionVisible(reg)) {
return reg;
}
return null;
});
}
protected void forVisibleRegions(Consumer<? super DBTraceMemoryRegion> action) {
for (long s : program.viewport.getOrderedSnaps()) {
// NOTE: This is slightly faster than new AddressSet(mm.getRegionsAddressSet(snap))
for (DBTraceMemoryRegion reg : memoryManager.getRegionsAtSnap(s)) {
if (program.isRegionVisible(reg)) {
action.accept(reg);
}
}
}
}
@Override
protected void recomputeAddressSet() {
AddressSet temp = new AddressSet();
// NOTE: This is slightly faster than new AddressSet(mm.getRegionsAddressSet(snap))
for (DBTraceMemoryRegion reg : memoryManager.getRegionsAtSnap(snap)) {
temp.add(reg.getRange());
}
// TODO: Performance test this
forVisibleRegions(reg -> temp.add(reg.getRange()));
addressSet = temp;
}
@ -49,26 +71,21 @@ public class DBTraceProgramViewMemory extends AbstractDBTraceProgramViewMemory {
@Override
public MemoryBlock getBlock(Address addr) {
DBTraceMemoryRegion region = memoryManager.getRegionContaining(snap, addr);
DBTraceMemoryRegion region = getTopRegion(s -> memoryManager.getRegionContaining(s, addr));
return region == null ? null : getBlock(region);
}
@Override
public MemoryBlock getBlock(String blockName) {
DBTraceMemoryRegion region = memoryManager.getLiveRegionByPath(snap, blockName);
DBTraceMemoryRegion region =
getTopRegion(s -> memoryManager.getLiveRegionByPath(s, blockName));
return region == null ? null : getBlock(region);
}
@Override
public MemoryBlock[] getBlocks() {
List<MemoryBlock> result = new ArrayList<>();
for (DBTraceMemoryRegion region : memoryManager.getRegionsInternal()) {
MemoryBlock block = getBlock(region);
if (block == null) {
continue;
}
result.add(block);
}
forVisibleRegions(reg -> result.add(getBlock(reg)));
return result.toArray(new MemoryBlock[result.size()]);
}

View file

@ -241,7 +241,7 @@ public class DBTraceProgramViewMemoryBlock implements MemoryBlock {
throw new MemoryAccessException("Space does not exist");
}
ByteBuffer buf = ByteBuffer.allocate(1);
if (space.getBytes(program.snap, addr, buf) != 1) {
if (space.getViewBytes(program.snap, addr, buf) != 1) {
throw new MemoryAccessException();
}
return buf.get(0);
@ -264,7 +264,7 @@ public class DBTraceProgramViewMemoryBlock implements MemoryBlock {
throw new MemoryAccessException("Space does not exist");
}
len = (int) Math.min(len, range.getMaxAddress().subtract(addr) + 1);
return space.getBytes(program.snap, addr, ByteBuffer.wrap(b, off, len));
return space.getViewBytes(program.snap, addr, ByteBuffer.wrap(b, off, len));
}
@Override

View file

@ -53,8 +53,11 @@ public class DBTraceProgramViewProgramContext extends AbstractProgramContext {
List<Register> registers = language.getRegisters();
List<Register> result = new ArrayList<>(registers.size());
for (Register register : registers) {
if (registerContextManager.hasRegisterValue(language, register, program.snap)) {
for (long s : program.viewport.getReversedSnaps()) {
if (registerContextManager.hasRegisterValue(language, register, s)) {
result.add(register);
break;
}
}
}
return result.toArray(new Register[result.size()]);
@ -66,10 +69,27 @@ public class DBTraceProgramViewProgramContext extends AbstractProgramContext {
return value == null ? null : signed ? value.getSignedValue() : value.getUnsignedValue();
}
protected RegisterValue combine(RegisterValue v1, RegisterValue v2) {
if (v1 == null) {
return v2;
}
else if (v2 == null) {
return v1;
}
return v1.combineValues(v2);
}
protected RegisterValue stack(RegisterValue value, Register register, Address address) {
for (long s : program.viewport.getReversedSnaps()) {
value = combine(value, registerContextManager.getValue(language, register, s, address));
}
return value;
}
@Override
public RegisterValue getRegisterValue(Register register, Address address) {
return registerContextManager.getValueWithDefault(language, register, program.snap,
address);
RegisterValue value = registerContextManager.getDefaultValue(language, register, address);
return stack(value, register, address);
}
@Override
@ -81,7 +101,8 @@ public class DBTraceProgramViewProgramContext extends AbstractProgramContext {
@Override
public RegisterValue getNonDefaultValue(Register register, Address address) {
return registerContextManager.getValue(language, register, program.snap, address);
RegisterValue value = new RegisterValue(register);
return stack(value, register, address);
}
@Override
@ -105,22 +126,24 @@ public class DBTraceProgramViewProgramContext extends AbstractProgramContext {
@Override
public AddressRangeIterator getRegisterValueAddressRanges(Register register) {
return registerContextManager.getRegisterValueAddressRanges(language, register,
program.snap).getAddressRanges();
return program.viewport.unionedAddresses(
s -> registerContextManager.getRegisterValueAddressRanges(language, register,
s)).getAddressRanges();
}
@Override
public AddressRangeIterator getRegisterValueAddressRanges(Register register, Address start,
Address end) {
return new NestedAddressRangeIterator<>(
language.getAddressFactory().getAddressSet(start, end).iterator(), range -> {
return registerContextManager.getRegisterValueAddressRanges(language, register,
program.snap, range).iterator();
});
language.getAddressFactory().getAddressSet(start, end).iterator(),
range -> program.viewport.unionedAddresses(
s -> registerContextManager.getRegisterValueAddressRanges(language, register,
s, range)).iterator());
}
@Override
public AddressRange getRegisterValueRangeContaining(Register register, Address address) {
// TODO: I don't know the value of making this work through the viewport.
Entry<TraceAddressSnapRange, RegisterValue> entry =
registerContextManager.getEntry(language, register, program.snap, address);
if (entry != null) {
@ -164,6 +187,7 @@ public class DBTraceProgramViewProgramContext extends AbstractProgramContext {
@Override
public boolean hasValueOverRange(Register register, BigInteger value,
AddressSetView addressSet) {
// TODO: Not sure the value of making this use the viewport
RegisterValue regVal = new RegisterValue(register, value);
try (LockHold hold = program.trace.lockRead()) {
AddressSet remains = new AddressSet(addressSet);

View file

@ -19,6 +19,7 @@ import com.google.common.collect.Range;
import ghidra.program.model.address.*;
import ghidra.trace.database.listing.DBTraceCodeRegisterSpace;
import ghidra.trace.database.listing.UndefinedDBTraceData;
import ghidra.trace.database.thread.DBTraceThread;
import ghidra.trace.model.program.TraceProgramViewRegisterListing;
import ghidra.util.exception.CancelledException;
@ -45,6 +46,11 @@ public class DBTraceProgramViewRegisterListing extends AbstractDBTraceProgramVie
return thread;
}
@Override
public UndefinedDBTraceData doCreateUndefinedUnit(Address address) {
throw new UnsupportedOperationException();
}
@Override
public boolean isUndefined(Address start, Address end) {
return codeOperations.undefinedData()

View file

@ -225,7 +225,7 @@ public class DBTraceProgramViewRegisterMemoryBlock implements MemoryBlock {
throw new MemoryAccessException();
}
ByteBuffer buf = ByteBuffer.allocate(1);
if (space.getBytes(program.snap, addr, buf) != 1) {
if (space.getViewBytes(program.snap, addr, buf) != 1) {
throw new MemoryAccessException();
}
return buf.get(0);
@ -242,7 +242,7 @@ public class DBTraceProgramViewRegisterMemoryBlock implements MemoryBlock {
throw new MemoryAccessException();
}
len = (int) Math.min(len, range.getMaxAddress().subtract(addr) + 1);
return space.getBytes(program.snap, addr, ByteBuffer.wrap(b, off, len));
return space.getViewBytes(program.snap, addr, ByteBuffer.wrap(b, off, len));
}
@Override

View file

@ -41,6 +41,7 @@ import ghidra.trace.model.Trace;
import ghidra.trace.model.data.TraceBasedDataTypeManager;
import ghidra.trace.model.program.TraceProgramView;
import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.util.TraceTimeViewport;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
@ -616,6 +617,11 @@ public class DBTraceProgramViewRegisters implements TraceProgramView {
return view.getSnap();
}
@Override
public TraceTimeViewport getViewport() {
return view.getViewport();
}
@Override
public Long getMaxSnap() {
return view.getMaxSnap();

View file

@ -17,11 +17,14 @@ package ghidra.trace.database.program;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.listing.*;
import ghidra.trace.database.memory.DBTraceMemoryRegion;
import ghidra.trace.model.memory.TraceMemoryRegion;
import ghidra.util.ComparatorMath;
import ghidra.util.LockHold;
import ghidra.util.exception.*;
@ -102,11 +105,10 @@ public class DBTraceProgramViewRootModule implements ProgramModule {
// NOTE: Would flush on snap change
try (LockHold hold = LockHold.lock(program.trace.getReadWriteLock().readLock())) {
List<DBTraceProgramViewFragment> frags = new ArrayList<>();
for (DBTraceMemoryRegion region : program.trace.getMemoryManager()
.getRegionsAtSnap(program.snap)) {
program.memory.forVisibleRegions(region -> {
frags.add(listing.fragmentsByRegion.computeIfAbsent(region,
r -> new DBTraceProgramViewFragment(listing, r)));
}
});
return frags.toArray(new DBTraceProgramViewFragment[frags.size()]);
}
}
@ -114,17 +116,11 @@ public class DBTraceProgramViewRootModule implements ProgramModule {
@Override
public int getIndex(String name) {
// TODO: This isn't pretty at all. Really should database these.
List<String> names = new ArrayList<>();
try (LockHold hold = LockHold.lock(program.trace.getReadWriteLock().readLock())) {
int i = 0;
for (DBTraceMemoryRegion region : program.trace.getMemoryManager()
.getRegionsAtSnap(program.snap)) {
if (name.equals(region.getName())) {
return i;
program.memory.forVisibleRegions(region -> names.add(region.getName()));
}
i++;
}
}
return -1;
return names.indexOf(names);
}
@Override
@ -173,14 +169,44 @@ public class DBTraceProgramViewRootModule implements ProgramModule {
return true;
}
protected <T> T reduceRegions(java.util.function.Function<TraceMemoryRegion, T> func,
BiFunction<T, T, T> reducer) {
var action = new Consumer<TraceMemoryRegion>() {
public T cur;
@Override
public void accept(TraceMemoryRegion region) {
if (cur == null) {
cur = func.apply(region);
}
else {
cur = reducer.apply(cur, func.apply(region));
}
}
};
return action.cur;
}
@Override
public Address getMinAddress() {
return program.trace.getMemoryManager().getRegionsAddressSet(program.snap).getMinAddress();
if (!program.viewport.isForked()) {
return program.trace.getMemoryManager()
.getRegionsAddressSet(program.snap)
.getMinAddress();
}
// TODO: There has got to be a better way
return reduceRegions(TraceMemoryRegion::getMinAddress, ComparatorMath::cmin);
}
@Override
public Address getMaxAddress() {
return program.trace.getMemoryManager().getRegionsAddressSet(program.snap).getMaxAddress();
if (!program.viewport.isForked()) {
return program.trace.getMemoryManager()
.getRegionsAddressSet(program.snap)
.getMaxAddress();
}
// TODO: There has got to be a better way
return reduceRegions(TraceMemoryRegion::getMaxAddress, ComparatorMath::cmax);
}
@Override
@ -195,7 +221,8 @@ public class DBTraceProgramViewRootModule implements ProgramModule {
@Override
public AddressSetView getAddressSet() {
return program.trace.getMemoryManager().getRegionsAddressSet(program.snap);
return program.viewport
.unionedAddresses(s -> program.trace.getMemoryManager().getRegionsAddressSet(s));
}
@Override

View file

@ -153,12 +153,16 @@ public class DBTraceProgramViewSymbolTable implements SymbolTable {
}
}
protected <T extends TraceSymbol> T requireInLifespan(T sym) {
protected <T extends TraceSymbol> T requireVisible(T sym) {
if (!(sym instanceof TraceSymbolWithLifespan)) {
return sym;
}
if (sym instanceof TraceFunctionSymbol) {
TraceFunctionSymbol function = (TraceFunctionSymbol) sym;
return program.isFunctionVisible(function, function.getLifespan()) ? sym : null;
}
TraceSymbolWithLifespan wl = (TraceSymbolWithLifespan) sym;
if (wl.getLifespan().contains(program.snap)) {
if (program.viewport.containsAnyUpper(wl.getLifespan())) {
return sym;
}
return null;
@ -166,7 +170,7 @@ public class DBTraceProgramViewSymbolTable implements SymbolTable {
@Override
public Symbol getSymbol(long symbolID) {
return requireInLifespan(symbolManager.getSymbolByID(symbolID));
return requireVisible(symbolManager.getSymbolByID(symbolID));
}
@Override
@ -178,7 +182,7 @@ public class DBTraceProgramViewSymbolTable implements SymbolTable {
if (!addr.equals(sym.getAddress())) {
continue;
}
if (requireInLifespan(sym) == null) {
if (requireVisible(sym) == null) {
continue;
}
return sym;
@ -198,7 +202,7 @@ public class DBTraceProgramViewSymbolTable implements SymbolTable {
for (TraceSymbol sym : symbolManager.allSymbols()
.getChildrenNamed(name,
assertTraceNamespace(namespace))) {
if (requireInLifespan(sym) == null) {
if (requireVisible(sym) == null) {
continue;
}
return sym;
@ -218,7 +222,7 @@ public class DBTraceProgramViewSymbolTable implements SymbolTable {
try (LockHold hold = program.trace.lockRead()) {
List<Symbol> result = new ArrayList<>();
for (TraceSymbol sym : symbolManager.allSymbols().getChildrenNamed(name, parent)) {
if (requireInLifespan(sym) != null) {
if (requireVisible(sym) != null) {
result.add(sym);
}
}
@ -239,7 +243,7 @@ public class DBTraceProgramViewSymbolTable implements SymbolTable {
for (TraceSymbol sym : symbolManager.labelsAndFunctions()
.getChildrenNamed(name,
parent)) {
if (requireInLifespan(sym) != null) {
if (requireVisible(sym) != null) {
result.add(sym);
}
}
@ -352,7 +356,7 @@ public class DBTraceProgramViewSymbolTable implements SymbolTable {
if (addr.isMemoryAddress()) {
return symbolManager.labelsAndFunctions().hasAt(program.snap, null, addr, true);
}
if (addr.isRegisterAddress() || addr.isStackAddress()) {
if (addr.getAddressSpace().isRegisterSpace() || addr.isStackAddress()) {
return symbolManager.allVariables().hasAt(addr, true);
}
return false;
@ -527,7 +531,7 @@ public class DBTraceProgramViewSymbolTable implements SymbolTable {
return sym.getParentNamespace();
}
}
if (addr.isRegisterAddress() || addr.isStackAddress()) {
if (addr.getAddressSpace().isRegisterSpace() || addr.isStackAddress()) {
for (TraceSymbol sym : symbolManager.allVariables().getAt(addr, true)) {
return sym.getParentNamespace();
}

Some files were not shown because too many files have changed in this diff Show more