mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 17:59:46 +02:00
GP-5266: Only track on user click or address change.
This commit is contained in:
parent
2a628178b3
commit
5d71f073f4
10 changed files with 135 additions and 32 deletions
|
@ -66,7 +66,7 @@ public class DebuggerCoordinates {
|
||||||
private static final String KEY_FRAME = "Frame";
|
private static final String KEY_FRAME = "Frame";
|
||||||
private static final String KEY_OBJ_PATH = "ObjectPath";
|
private static final String KEY_OBJ_PATH = "ObjectPath";
|
||||||
|
|
||||||
public static boolean equalsIgnoreRecorderAndView(DebuggerCoordinates a,
|
public static boolean equalsIgnoreTargetAndView(DebuggerCoordinates a,
|
||||||
DebuggerCoordinates b) {
|
DebuggerCoordinates b) {
|
||||||
if (!Objects.equals(a.trace, b.trace)) {
|
if (!Objects.equals(a.trace, b.trace)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -417,6 +417,36 @@ public class DebuggerCoordinates {
|
||||||
newFrame, newPath);
|
newFrame, newPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the given coordinates are the same as this but with an extra or differing patch.
|
||||||
|
*
|
||||||
|
* @param that the other coordinates
|
||||||
|
* @return true if the difference is only in the final patch step
|
||||||
|
*/
|
||||||
|
public boolean differsOnlyByPatch(DebuggerCoordinates that) {
|
||||||
|
if (!Objects.equals(this.trace, that.trace)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Objects.equals(this.platform, that.platform)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!Objects.equals(this.thread, that.thread)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Consider defaults
|
||||||
|
if (!Objects.equals(this.getFrame(), that.getFrame())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!Objects.equals(this.getObject(), that.getObject())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!this.getTime().differsOnlyByPatch(that.getTime())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public DebuggerCoordinates frame(int newFrame) {
|
public DebuggerCoordinates frame(int newFrame) {
|
||||||
if (trace == null) {
|
if (trace == null) {
|
||||||
return NOWHERE;
|
return NOWHERE;
|
||||||
|
@ -621,7 +651,11 @@ public class DebuggerCoordinates {
|
||||||
if (registerContainer != null) {
|
if (registerContainer != null) {
|
||||||
return registerContainer;
|
return registerContainer;
|
||||||
}
|
}
|
||||||
return registerContainer = getObject().findRegisterContainer(getFrame());
|
TraceObject object = getObject();
|
||||||
|
if (object == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return registerContainer = object.findRegisterContainer(getFrame());
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized long getViewSnap() {
|
public synchronized long getViewSnap() {
|
||||||
|
|
|
@ -52,6 +52,10 @@ public class DebuggerTrackLocationTrait {
|
||||||
protected static final AutoConfigState.ClassHandler<DebuggerTrackLocationTrait> CONFIG_STATE_HANDLER =
|
protected static final AutoConfigState.ClassHandler<DebuggerTrackLocationTrait> CONFIG_STATE_HANDLER =
|
||||||
AutoConfigState.wireHandler(DebuggerTrackLocationTrait.class, MethodHandles.lookup());
|
AutoConfigState.wireHandler(DebuggerTrackLocationTrait.class, MethodHandles.lookup());
|
||||||
|
|
||||||
|
public enum TrackCause {
|
||||||
|
USER, DB_CHANGE, NAVIGATION, EMU_PATCH, SPEC_CHANGE_API;
|
||||||
|
}
|
||||||
|
|
||||||
protected class ForTrackingListener extends TraceDomainObjectListener {
|
protected class ForTrackingListener extends TraceDomainObjectListener {
|
||||||
|
|
||||||
public ForTrackingListener() {
|
public ForTrackingListener() {
|
||||||
|
@ -68,7 +72,7 @@ public class DebuggerTrackLocationTrait {
|
||||||
if (!tracker.affectedByBytesChange(space, range, current)) {
|
if (!tracker.affectedByBytesChange(space, range, current)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
doTrack();
|
doTrack(TrackCause.DB_CHANGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void stackChanged(TraceStack stack) {
|
private void stackChanged(TraceStack stack) {
|
||||||
|
@ -79,7 +83,7 @@ public class DebuggerTrackLocationTrait {
|
||||||
if (!tracker.affectedByStackChange(stack, current)) {
|
if (!tracker.affectedByStackChange(stack, current)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
doTrack();
|
doTrack(TrackCause.DB_CHANGE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,11 +192,11 @@ public class DebuggerTrackLocationTrait {
|
||||||
public void setSpec(LocationTrackingSpec spec) {
|
public void setSpec(LocationTrackingSpec spec) {
|
||||||
if (action == null) {
|
if (action == null) {
|
||||||
// It might if the client doesn't need a new button, e.g., TraceDiff
|
// It might if the client doesn't need a new button, e.g., TraceDiff
|
||||||
doSetSpec(spec);
|
doSetSpec(spec, TrackCause.SPEC_CHANGE_API);
|
||||||
}
|
}
|
||||||
else if (!hasSpec(spec)) {
|
else if (!hasSpec(spec)) {
|
||||||
Msg.warn(this, "No action state for given tracking spec: " + spec);
|
Msg.warn(this, "No action state for given tracking spec: " + spec);
|
||||||
doSetSpec(spec);
|
doSetSpec(spec, TrackCause.SPEC_CHANGE_API);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
action.setCurrentActionStateByUserData(spec);
|
action.setCurrentActionStateByUserData(spec);
|
||||||
|
@ -234,21 +238,21 @@ public class DebuggerTrackLocationTrait {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void clickedSpecButton(ActionContext ctx) {
|
protected void clickedSpecButton(ActionContext ctx) {
|
||||||
doTrack();
|
doTrack(TrackCause.USER);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void clickedSpecMenu(ActionState<LocationTrackingSpec> newState,
|
protected void clickedSpecMenu(ActionState<LocationTrackingSpec> newState,
|
||||||
EventTrigger trigger) {
|
EventTrigger trigger) {
|
||||||
doSetSpec(newState.getUserData());
|
doSetSpec(newState.getUserData(), TrackCause.USER);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void doSetSpec(LocationTrackingSpec spec) {
|
protected void doSetSpec(LocationTrackingSpec spec, TrackCause cause) {
|
||||||
if (this.spec != spec) {
|
if (this.spec != spec) {
|
||||||
this.spec = spec;
|
this.spec = spec;
|
||||||
this.tracker = spec.getTracker();
|
this.tracker = spec.getTracker();
|
||||||
specChanged(spec);
|
specChanged(spec);
|
||||||
}
|
}
|
||||||
doTrack();
|
doTrack(cause);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ProgramLocation computeTrackedLocation() {
|
protected ProgramLocation computeTrackedLocation() {
|
||||||
|
@ -282,9 +286,15 @@ public class DebuggerTrackLocationTrait {
|
||||||
return spec.getLocationLabel() + " = " + trackedLocation.getByteAddress();
|
return spec.getLocationLabel() + " = " + trackedLocation.getByteAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void doTrack() {
|
protected void doTrack(TrackCause cause) {
|
||||||
try {
|
try {
|
||||||
trackedLocation = computeTrackedLocation();
|
ProgramLocation newLocation = computeTrackedLocation();
|
||||||
|
if (Objects.equals(newLocation, trackedLocation)) {
|
||||||
|
if (cause == TrackCause.DB_CHANGE || cause == TrackCause.EMU_PATCH) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trackedLocation = newLocation;
|
||||||
locationTracked();
|
locationTracked();
|
||||||
}
|
}
|
||||||
catch (Throwable ex) {
|
catch (Throwable ex) {
|
||||||
|
@ -315,11 +325,12 @@ public class DebuggerTrackLocationTrait {
|
||||||
if (doListeners) {
|
if (doListeners) {
|
||||||
removeOldListeners();
|
removeOldListeners();
|
||||||
}
|
}
|
||||||
|
boolean isPatch = current.differsOnlyByPatch(coordinates);
|
||||||
current = coordinates;
|
current = coordinates;
|
||||||
if (doListeners) {
|
if (doListeners) {
|
||||||
addNewListeners();
|
addNewListeners();
|
||||||
}
|
}
|
||||||
doTrack();
|
doTrack(isPatch ? TrackCause.EMU_PATCH : TrackCause.NAVIGATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeConfigState(SaveState saveState) {
|
public void writeConfigState(SaveState saveState) {
|
||||||
|
|
|
@ -235,7 +235,7 @@ public class DebuggerListingProvider extends CodeViewerProvider {
|
||||||
super(DebuggerListingProvider.this.tool, DebuggerListingProvider.this.plugin,
|
super(DebuggerListingProvider.this.tool, DebuggerListingProvider.this.plugin,
|
||||||
DebuggerListingProvider.this);
|
DebuggerListingProvider.this);
|
||||||
|
|
||||||
getListingPanel().addIndexMapChangeListener(e -> this.doTrack());
|
getListingPanel().addIndexMapChangeListener(e -> this.doTrack(TrackCause.DB_CHANGE));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -90,7 +90,7 @@ public abstract class AbstractQueryTablePanel<T, M extends AbstractQueryTableMod
|
||||||
}
|
}
|
||||||
|
|
||||||
public void goToCoordinates(DebuggerCoordinates coords) {
|
public void goToCoordinates(DebuggerCoordinates coords) {
|
||||||
if (DebuggerCoordinates.equalsIgnoreRecorderAndView(current, coords)) {
|
if (DebuggerCoordinates.equalsIgnoreTargetAndView(current, coords)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
DebuggerCoordinates previous = current;
|
DebuggerCoordinates previous = current;
|
||||||
|
|
|
@ -234,7 +234,7 @@ public class ObjectsTreePanel extends JPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void goToCoordinates(DebuggerCoordinates coords) {
|
public void goToCoordinates(DebuggerCoordinates coords) {
|
||||||
if (DebuggerCoordinates.equalsIgnoreRecorderAndView(current, coords)) {
|
if (DebuggerCoordinates.equalsIgnoreTargetAndView(current, coords)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
previous = current;
|
previous = current;
|
||||||
|
|
|
@ -470,4 +470,25 @@ public class Sequence implements Comparable<Sequence> {
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean differsOnlyByPatch(Sequence that) {
|
||||||
|
int size = this.steps.size();
|
||||||
|
if (size == that.steps.size()) {
|
||||||
|
if (size == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!this.steps.subList(0, size - 1).equals(that.steps.subList(0, size - 1))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Step thisLast = this.steps.getLast();
|
||||||
|
Step thatLast = that.steps.getLast();
|
||||||
|
return thisLast.equals(thatLast) ||
|
||||||
|
thisLast instanceof PatchStep && thatLast instanceof PatchStep;
|
||||||
|
}
|
||||||
|
if (size == that.steps.size() - 1) {
|
||||||
|
Step thatLast = that.steps.getLast();
|
||||||
|
return thatLast instanceof PatchStep;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -673,4 +673,20 @@ public class TraceSchedule implements Comparable<TraceSchedule> {
|
||||||
public TraceSchedule assumeRecorded() {
|
public TraceSchedule assumeRecorded() {
|
||||||
return new TraceSchedule(snap, steps, pSteps, Source.RECORD);
|
return new TraceSchedule(snap, steps, pSteps, Source.RECORD);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean differsOnlyByPatch(TraceSchedule that) {
|
||||||
|
if (this.snap != that.snap) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.pSteps.isNop() != that.pSteps.isNop()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.pSteps.isNop()) {
|
||||||
|
return this.steps.differsOnlyByPatch(that.steps);
|
||||||
|
}
|
||||||
|
if (!this.steps.equals(that.steps)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return this.pSteps.differsOnlyByPatch(that.pSteps);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -470,4 +470,37 @@ public class TraceScheduleTest extends AbstractGhidraHeadlessIntegrationTest {
|
||||||
"t0-{r0=0x200000001};t0-{r1l=0x3}", time.toString());
|
"t0-{r0=0x200000001};t0-{r1l=0x3}", time.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDiffersOnlyByPatch() throws Exception {
|
||||||
|
assertTrue(TraceSchedule.parse("1").differsOnlyByPatch(TraceSchedule.parse("1")));
|
||||||
|
assertTrue(TraceSchedule.parse("1:1").differsOnlyByPatch(TraceSchedule.parse("1:1")));
|
||||||
|
assertTrue(TraceSchedule.parse("1:1.1").differsOnlyByPatch(TraceSchedule.parse("1:1.1")));
|
||||||
|
assertTrue(TraceSchedule.parse("1:1;{r0=1}")
|
||||||
|
.differsOnlyByPatch(TraceSchedule.parse("1:1;{r0=1}")));
|
||||||
|
assertTrue(TraceSchedule.parse("1:1.1;{r0=1}")
|
||||||
|
.differsOnlyByPatch(TraceSchedule.parse("1:1.1;{r0=1}")));
|
||||||
|
|
||||||
|
assertFalse(TraceSchedule.parse("1").differsOnlyByPatch(TraceSchedule.parse("1:1")));
|
||||||
|
assertFalse(TraceSchedule.parse("1:1").differsOnlyByPatch(TraceSchedule.parse("1")));
|
||||||
|
|
||||||
|
assertFalse(TraceSchedule.parse("1:1").differsOnlyByPatch(TraceSchedule.parse("1:2")));
|
||||||
|
assertFalse(TraceSchedule.parse("1:2").differsOnlyByPatch(TraceSchedule.parse("1:1")));
|
||||||
|
|
||||||
|
assertFalse(TraceSchedule.parse("1:1").differsOnlyByPatch(TraceSchedule.parse("1:1.1")));
|
||||||
|
assertFalse(TraceSchedule.parse("1:1.1").differsOnlyByPatch(TraceSchedule.parse("1:1")));
|
||||||
|
|
||||||
|
assertTrue(TraceSchedule.parse("1").differsOnlyByPatch(TraceSchedule.parse("1:{r0=1}")));
|
||||||
|
assertFalse(TraceSchedule.parse("1:{r0=1}").differsOnlyByPatch(TraceSchedule.parse("1")));
|
||||||
|
|
||||||
|
assertTrue(
|
||||||
|
TraceSchedule.parse("1:1").differsOnlyByPatch(TraceSchedule.parse("1:1;{r0=1}")));
|
||||||
|
assertFalse(
|
||||||
|
TraceSchedule.parse("1:1;{r0=1}").differsOnlyByPatch(TraceSchedule.parse("1:1")));
|
||||||
|
|
||||||
|
assertTrue(
|
||||||
|
TraceSchedule.parse("1:1.1").differsOnlyByPatch(TraceSchedule.parse("1:1.1;{r0=1}")));
|
||||||
|
assertFalse(
|
||||||
|
TraceSchedule.parse("1:1.1;{r0=1}").differsOnlyByPatch(TraceSchedule.parse("1:1.1")));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -295,13 +295,6 @@ section of <code>termmines</code> in the Static Listing, the Dynamic
|
||||||
Listing will follow along showing you the live values in memory. You can
|
Listing will follow along showing you the live values in memory. You can
|
||||||
also experiment by placing code units in the Dynamic Listing before
|
also experiment by placing code units in the Dynamic Listing before
|
||||||
committing to them in the Static Listing.</p>
|
committing to them in the Static Listing.</p>
|
||||||
<p><strong>NOTE</strong>: There’s a known issue with auto-seek obtruding
|
|
||||||
user navigation in the listings. In most cases, just navigating again
|
|
||||||
will make it stick. If it becomes a real annoyance, set the
|
|
||||||
<strong>Auto-Track</strong> drop-down in the top right of the Dynamic
|
|
||||||
Listing to <strong>Do Not Track</strong> while you’re doing static RE.
|
|
||||||
Be sure to put it back to <strong>Track Program Counter</strong> when
|
|
||||||
you are done.</p>
|
|
||||||
<section id="questions" class="level4">
|
<section id="questions" class="level4">
|
||||||
<h4>Questions:</h4>
|
<h4>Questions:</h4>
|
||||||
<ol type="1">
|
<ol type="1">
|
||||||
|
|
|
@ -137,11 +137,6 @@ Because you are in a dynamic session, you have an example board to work with.
|
||||||
As you navigate the `.data` section of `termmines` in the Static Listing, the Dynamic Listing will follow along showing you the live values in memory.
|
As you navigate the `.data` section of `termmines` in the Static Listing, the Dynamic Listing will follow along showing you the live values in memory.
|
||||||
You can also experiment by placing code units in the Dynamic Listing before committing to them in the Static Listing.
|
You can also experiment by placing code units in the Dynamic Listing before committing to them in the Static Listing.
|
||||||
|
|
||||||
**NOTE**: There's a known issue with auto-seek obtruding user navigation in the listings.
|
|
||||||
In most cases, just navigating again will make it stick.
|
|
||||||
If it becomes a real annoyance, set the **Auto-Track** drop-down in the top right of the Dynamic Listing to **Do Not Track** while you're doing static RE.
|
|
||||||
Be sure to put it back to **Track Program Counter** when you are done.
|
|
||||||
|
|
||||||
#### Questions:
|
#### Questions:
|
||||||
|
|
||||||
1. How are the cells allocated?
|
1. How are the cells allocated?
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue