GP-5266: Only track on user click or address change.

This commit is contained in:
Dan 2025-01-15 13:56:40 -05:00 committed by Ryan Kurtz
parent 2a628178b3
commit 5d71f073f4
10 changed files with 135 additions and 32 deletions

View file

@ -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() {

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -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) {

View file

@ -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

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -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;

View file

@ -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;

View file

@ -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;
}
} }

View file

@ -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);
}
} }

View file

@ -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")));
}
} }

View file

@ -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>: Theres 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 youre 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">

View file

@ -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?