Merge remote-tracking branch 'origin/GP-2459_Dan_pcSpTrackingNewConventions--SQUASHED'

This commit is contained in:
Ryan Kurtz 2022-09-24 01:49:46 -04:00
commit 336b0531bc
14 changed files with 304 additions and 84 deletions

View file

@ -108,6 +108,19 @@
meaning may vary among platforms. While there may be some nuances to the register value, the
value recorded in the stack should indicate the next instruction to be executed.</LI>
<LI>Track Program Counter (by stack) - navigates this listing to the current program counter
as recorded from the debugger's stack trace. This option is not recommended for regular use,
especially for emulation, since the emulator does not provide stack trace records. It is
recommended for debugger connection developers to verify the stack records are being property
interpreted by the GUI.</LI>
<LI>Track Program Counter (by register) - navigates this listing to the current program
counter as recorded from the registers. While suitable for regular use, the default "Track
Program Counter" option is recommended, since the stack may record the program counter in
some cases where the registers do not. For example, when unwinding a stack frame, the
debugger may report the frame's program counter without reporting the frame's registers. This
option may be desired if/when stack frames are recorded incorrectly.</LI>
<LI>Track Stack Pointer - navigates this listing to the current stack pointer. Again,
platforms may vary in how they define the stack pointer.</LI>
</UL>

View file

@ -83,13 +83,26 @@
<UL>
<LI>Do Not Track - disables automatic navigation.</LI>
<LI>Track Program Counter - (default) navigates this window to the current program counter.
<LI>Track Program Counter - (default) navigates this listing to the current program counter.
If the stack contains a record of the program counter, it is preferred to the recorded
register value. Note that debuggers may vary in how they report the program counter, and its
meaning may vary among platforms. While there may be some nuances to the register value, the
value recorded in the stack should indicate the next instruction to be executed.</LI>
<LI>Track Stack Pointer - navigates this window to the current stack pointer. Again,
<LI>Track Program Counter (by stack) - navigates this listing to the current program counter
as recorded from the debugger's stack trace. This option is not recommended for regular use,
especially for emulation, since the emulator does not provide stack trace records. It is
recommended for debugger connection developers to verify the stack records are being property
interpreted by the GUI.</LI>
<LI>Track Program Counter (by register) - navigates this listing to the current program
counter as recorded from the registers. While suitable for regular use, the default "Track
Program Counter" option is recommended, since the stack may record the program counter in
some cases where the registers do not. For example, when unwinding a stack frame, the
debugger may report the frame's program counter without reporting the frame's registers. This
option may be desired if/when stack frames are recorded incorrectly.</LI>
<LI>Track Stack Pointer - navigates this listing to the current stack pointer. Again,
platforms may vary in how they define the stack pointer.</LI>
</UL>

View file

@ -859,11 +859,15 @@ public interface DebuggerResources {
String HELP_ANCHOR = "track_location";
String NAME_PC = "Track Program Counter";
String NAME_PC_BY_REGISTER = "Track Program Counter (by Register)";
String NAME_PC_BY_STACK = "Track Program Counter (by Stack)";
String NAME_SP = "Track Stack Pointer";
String NAME_NONE = "Do Not Track";
// TODO: Separate icons for Program Counter and Stack Pointer
Icon ICON_PC = ICON_REGISTER_MARKER;
Icon ICON_PC_BY_REGISTER = ICON_REGISTER_MARKER;
Icon ICON_PC_BY_STACK = ICON_REGISTER_MARKER;
Icon ICON_SP = ICON_REGISTER_MARKER;
// TODO: Consider sync_disabled icon
Icon ICON_NONE = ICON_DELETE;

View file

@ -25,6 +25,7 @@ public interface DebuggerTrackLocationAction extends TrackLocationAction {
static MultiStateActionBuilder<LocationTrackingSpec> builder(Plugin owner) {
MultiStateActionBuilder<LocationTrackingSpec> builder = TrackLocationAction.builder(owner);
builder.toolBarGroup(owner.getName());
builder.useCheckboxForIcons(true);
for (LocationTrackingSpec spec : LocationTrackingSpec.allSpecs().values()) {
builder.addState(spec.getMenuName(), spec.getMenuIcon(), spec);
}

View file

@ -251,8 +251,8 @@ public class DebuggerTrackLocationTrait {
return null;
}
// NB: view's snap may be forked for emulation
Address address = spec.computeTraceAddress(tool, cur, current.getView().getSnap());
return address == null ? null : new ProgramLocation(current.getView(), address);
Address address = spec.computeTraceAddress(tool, cur);
return address == null ? null : new ProgramLocation(cur.getView(), address);
}
protected void doTrack() {

View file

@ -29,21 +29,26 @@ import ghidra.framework.plugintool.AutoConfigState.ConfigFieldCodec;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.Address;
import ghidra.trace.model.TraceAddressSnapRange;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.memory.TraceMemorySpace;
import ghidra.trace.model.stack.TraceStack;
import ghidra.trace.util.TraceAddressSpace;
import ghidra.util.classfinder.ClassSearcher;
import ghidra.util.classfinder.ExtensionPoint;
/**
* A "specification" for automatic navigation of the dynamic listing
* A specification for automatic navigation of the dynamic listing
*
* <p>
* TODO: Some of these should be configurable, and permit multiple instances, so that common
* configurations can be saved. The most obvious use case would be a SLEIGH expression. A user may
* want 3 different common expressions readily available in the drop-down list.
* TODO: Some of these should be configurable and permit multiple instances so that common
* configurations can be saved. The most obvious use case would be a Sleigh expression. A user may
* want 3 different common expressions readily available in the drop-down list. It might make sense
* to generate a tracking specification from each Watch.
*/
public interface LocationTrackingSpec extends ExtensionPoint {
class Private {
enum Private {
INSTANCE;
private final Map<String, LocationTrackingSpec> specsByName = new TreeMap<>();
private final ChangeListener classListener = this::classesChanged;
@ -57,7 +62,7 @@ public interface LocationTrackingSpec extends ExtensionPoint {
}
}
Private PRIVATE = new Private();
Private PRIVATE = Private.INSTANCE;
public static class TrackingSpecConfigFieldCodec
implements ConfigFieldCodec<LocationTrackingSpec> {
@ -74,13 +79,32 @@ public interface LocationTrackingSpec extends ExtensionPoint {
}
}
/**
* Check if the given trace-space and range refer to memory or the current frame
*
* <p>
* If the space models memory, the thread and frame are not considered, in case, e.g., the
* tracked register is memory-mapped. If the space models registers, the thread and frame are
* considered and must match those given in the coordinates. Whatever the case, the span must
* include the snap of the coordinates. Otherwise, the change is not considered current.
*
* @param space the trace-space, giving thread, frame, and address space
* @param range the address range and time span of the change
* @param current the current coordinates
* @return true if the change affects the tracked address for the given coordinates
*/
static boolean changeIsCurrent(TraceAddressSpace space, TraceAddressSnapRange range,
DebuggerCoordinates current) {
if (space == null || space.getThread() != current.getThread()) {
if (space == null) {
return false;
}
if (space.getFrameLevel() != current.getFrame()) {
return false;
if (!space.getAddressSpace().isMemorySpace()) {
TraceMemorySpace memSpace = current.getTrace()
.getMemoryManager()
.getMemoryRegisterSpace(current.getThread(), current.getFrame(), false);
if (memSpace == null || memSpace.getAddressSpace() != space.getAddressSpace()) {
return false;
}
}
if (!range.getLifespan().contains(current.getSnap())) {
return false;
@ -88,22 +112,54 @@ public interface LocationTrackingSpec extends ExtensionPoint {
return true;
}
/**
* Get the specification for the given configuration name
*
* @param name the name
* @return the spec, or null
*/
static LocationTrackingSpec fromConfigName(String name) {
synchronized (PRIVATE) {
return PRIVATE.specsByName.get(name);
}
}
/**
* Get a copy of all the known specifications
*
* @return the specifications by configuration name
*/
static Map<String, LocationTrackingSpec> allSpecs() {
synchronized (PRIVATE) {
return Map.copyOf(PRIVATE.specsByName);
return new TreeMap<>(PRIVATE.specsByName);
}
}
/**
* Get the configuration name
*
* <p>
* This is the value stored in configuration files to identify this specification
*
* @return the configuration name
*/
String getConfigName();
/**
* A human-readable name for this specification
*
* <p>
* This is the text displayed in menus
*
* @return the menu name
*/
String getMenuName();
/**
* Get the icon for this specification
*
* @return the icon
*/
Icon getMenuIcon();
/**
@ -120,15 +176,16 @@ public interface LocationTrackingSpec extends ExtensionPoint {
* <p>
* If the coordinates indicate emulation, i.e., the schedule is non-empty, the trace manager
* will already have performed the emulation and stored the results in a "scratch" snap. In
* general, the location should be computed using that snap (@code emuSnap) rather than the one
* indicated in {@code coordinates}.
* general, the location should be computed using that snap, i.e.,
* {@link DebuggerCoordinates#getViewSnap()} rather than {@link DebuggerCoordinates#getSnap()}.
* The address returned must be in the host platform's language, i.e., please use
* {@link TracePlatform#mapGuestToHost(Address)}.
*
* @param tool the tool containing the provider
* @param coordinates the trace, thread, snap, etc., of the tool
* @param emuSnap the "scratch" snap storing emulated state
* @return the address to navigate to
*/
Address computeTraceAddress(PluginTool tool, DebuggerCoordinates coordinates, long emuSnap);
Address computeTraceAddress(PluginTool tool, DebuggerCoordinates coordinates);
// TODO: Is there a way to generalize these so that other dependencies need not
// have their own bespoke methods?

View file

@ -49,8 +49,7 @@ public class NoneLocationTrackingSpec implements LocationTrackingSpec {
}
@Override
public Address computeTraceAddress(PluginTool tool, DebuggerCoordinates coordinates,
long emuSnap) {
public Address computeTraceAddress(PluginTool tool, DebuggerCoordinates coordinates) {
return null;
}

View file

@ -0,0 +1,57 @@
/* ###
* 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.action;
import javax.swing.Icon;
import ghidra.app.plugin.core.debug.DebuggerCoordinates;
import ghidra.app.plugin.core.debug.gui.DebuggerResources.TrackLocationAction;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.lang.Register;
import ghidra.trace.model.guest.TracePlatform;
public class PCByRegisterLocationTrackingSpec implements RegisterLocationTrackingSpec {
public static final String CONFIG_NAME = "TRACK_PC_BY_REGISTER";
@Override
public String getConfigName() {
return CONFIG_NAME;
}
@Override
public String getMenuName() {
return TrackLocationAction.NAME_PC_BY_REGISTER;
}
@Override
public Icon getMenuIcon() {
return TrackLocationAction.ICON_PC_BY_REGISTER;
}
@Override
public Register computeRegister(DebuggerCoordinates coordinates) {
TracePlatform platform = coordinates.getPlatform();
if (platform == null) {
return null;
}
return platform.getLanguage().getProgramCounter();
}
@Override
public AddressSpace computeDefaultAddressSpace(DebuggerCoordinates coordinates) {
return coordinates.getPlatform().getLanguage().getDefaultSpace();
}
}

View file

@ -0,0 +1,96 @@
/* ###
* 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.action;
import javax.swing.Icon;
import ghidra.app.plugin.core.debug.DebuggerCoordinates;
import ghidra.app.plugin.core.debug.gui.DebuggerResources.TrackLocationAction;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.Address;
import ghidra.trace.model.Trace;
import ghidra.trace.model.TraceAddressSnapRange;
import ghidra.trace.model.stack.TraceStack;
import ghidra.trace.model.stack.TraceStackFrame;
import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.util.TraceAddressSpace;
public class PCByStackLocationTrackingSpec implements LocationTrackingSpec {
public static final String CONFIG_NAME = "TRACK_PC_BY_STACK";
@Override
public String getConfigName() {
return CONFIG_NAME;
}
@Override
public String getMenuName() {
return TrackLocationAction.NAME_PC_BY_STACK;
}
@Override
public Icon getMenuIcon() {
return TrackLocationAction.ICON_PC_BY_STACK;
}
@Override
public String computeTitle(DebuggerCoordinates coordinates) {
return "Stack's PC";
}
@Override
public Address computeTraceAddress(PluginTool tool, DebuggerCoordinates coordinates) {
Trace trace = coordinates.getTrace();
TraceThread thread = coordinates.getThread();
long snap = coordinates.getSnap();
TraceStack stack = trace.getStackManager().getLatestStack(thread, snap);
if (stack == null) {
return null;
}
int level = coordinates.getFrame();
TraceStackFrame frame = stack.getFrame(level, false);
if (frame == null) {
return null;
}
return frame.getProgramCounter(snap);
}
// Note it does no good to override affectByRegChange. It must do what we'd avoid anyway.
@Override
public boolean affectedByStackChange(TraceStack stack, DebuggerCoordinates coordinates) {
if (stack.getThread() != coordinates.getThread()) {
return false;
}
if (!coordinates.getTime().isSnapOnly()) {
return false;
}
// TODO: Would be nice to have stack lifespan...
// TODO: It does in objects mode. Leave until old code is removed.
TraceStack curStack = coordinates.getTrace()
.getStackManager()
.getLatestStack(stack.getThread(), coordinates.getSnap());
if (stack != curStack) {
return false;
}
return true;
}
@Override
public boolean affectedByRegisterChange(TraceAddressSpace space, TraceAddressSnapRange range,
DebuggerCoordinates coordinates) {
return false;
}
}

View file

@ -21,16 +21,18 @@ import ghidra.app.plugin.core.debug.DebuggerCoordinates;
import ghidra.app.plugin.core.debug.gui.DebuggerResources.TrackLocationAction;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.lang.Register;
import ghidra.trace.model.Trace;
import ghidra.trace.model.TraceAddressSnapRange;
import ghidra.trace.model.stack.TraceStack;
import ghidra.trace.model.stack.TraceStackFrame;
import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.util.TraceAddressSpace;
public class PCLocationTrackingSpec implements RegisterLocationTrackingSpec {
public class PCLocationTrackingSpec implements LocationTrackingSpec {
public static final String CONFIG_NAME = "TRACK_PC";
private static final PCByRegisterLocationTrackingSpec BY_REG =
new PCByRegisterLocationTrackingSpec();
private static final PCByStackLocationTrackingSpec BY_STACK =
new PCByStackLocationTrackingSpec();
@Override
public String getConfigName() {
return CONFIG_NAME;
@ -47,63 +49,30 @@ public class PCLocationTrackingSpec implements RegisterLocationTrackingSpec {
}
@Override
public Register computeRegister(DebuggerCoordinates coordinates) {
Trace trace = coordinates.getTrace();
if (trace == null) {
return null;
}
return trace.getBaseLanguage().getProgramCounter();
public String computeTitle(DebuggerCoordinates coordinates) {
return "Auto PC";
}
@Override
public AddressSpace computeDefaultAddressSpace(DebuggerCoordinates coordinates) {
return coordinates.getTrace().getBaseLanguage().getDefaultSpace();
}
public Address computePCViaStack(DebuggerCoordinates coordinates) {
Trace trace = coordinates.getTrace();
TraceThread thread = coordinates.getThread();
long snap = coordinates.getSnap();
TraceStack stack = trace.getStackManager().getLatestStack(thread, snap);
if (stack == null) {
return null;
}
int level = coordinates.getFrame();
TraceStackFrame frame = stack.getFrame(level, false);
if (frame == null) {
return null;
}
return frame.getProgramCounter(snap);
}
@Override
public Address computeTraceAddress(PluginTool tool, DebuggerCoordinates coordinates,
long emuSnap) {
public Address computeTraceAddress(PluginTool tool, DebuggerCoordinates coordinates) {
if (coordinates.getTime().isSnapOnly()) {
Address pc = computePCViaStack(coordinates);
Address pc = BY_STACK.computeTraceAddress(tool, coordinates);
if (pc != null) {
return pc;
}
}
return RegisterLocationTrackingSpec.super.computeTraceAddress(tool, coordinates, emuSnap);
return BY_REG.computeTraceAddress(tool, coordinates);
}
// Note it does no good to override affectByRegChange. It must do what we'd avoid anyway.
@Override
public boolean affectedByStackChange(TraceStack stack, DebuggerCoordinates coordinates) {
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()
.getLatestStack(stack.getThread(), coordinates.getSnap());
if (stack != curStack) {
return false;
}
return true;
return BY_STACK.affectedByStackChange(stack, coordinates);
}
@Override
public boolean affectedByRegisterChange(TraceAddressSpace space, TraceAddressSnapRange range,
DebuggerCoordinates coordinates) {
return BY_REG.affectedByRegisterChange(space, range, coordinates);
}
}

View file

@ -22,12 +22,12 @@ import ghidra.program.model.lang.Register;
import ghidra.program.model.lang.RegisterValue;
import ghidra.trace.model.Trace;
import ghidra.trace.model.TraceAddressSnapRange;
import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.memory.TraceMemorySpace;
import ghidra.trace.model.memory.TraceMemoryState;
import ghidra.trace.model.stack.TraceStack;
import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.util.TraceAddressSpace;
import ghidra.trace.util.TraceRegisterUtils;
// TODO: Use this, or allow arbitrary expressions
public interface RegisterLocationTrackingSpec extends LocationTrackingSpec {
@ -45,10 +45,11 @@ public interface RegisterLocationTrackingSpec extends LocationTrackingSpec {
}
@Override
default Address computeTraceAddress(PluginTool tool, DebuggerCoordinates coordinates,
long emuSnap) {
default Address computeTraceAddress(PluginTool tool, DebuggerCoordinates coordinates) {
Trace trace = coordinates.getTrace();
TracePlatform platform = coordinates.getPlatform();
TraceThread thread = coordinates.getThread();
long viewSnap = coordinates.getViewSnap();
long snap = coordinates.getSnap();
int frame = coordinates.getFrame();
Register reg = computeRegister(coordinates);
@ -64,19 +65,19 @@ public interface RegisterLocationTrackingSpec extends LocationTrackingSpec {
return null;
}
RegisterValue value;
if (regs.getState(emuSnap, reg) == TraceMemoryState.KNOWN) {
value = regs.getValue(emuSnap, reg);
if (regs.getState(platform, viewSnap, reg) == TraceMemoryState.KNOWN) {
value = regs.getValue(platform, viewSnap, reg);
}
else {
value = regs.getValue(snap, reg);
value = regs.getValue(platform, snap, reg);
}
if (value == null) {
return null;
}
// TODO: Action to select the address space
// Could use code unit, but that can't specify space, yet, either....
return computeDefaultAddressSpace(coordinates)
.getAddress(value.getUnsignedValue().longValue(), true);
return platform.mapGuestToHost(computeDefaultAddressSpace(coordinates)
.getAddress(value.getUnsignedValue().longValue(), true));
}
@Override
@ -89,7 +90,9 @@ public interface RegisterLocationTrackingSpec extends LocationTrackingSpec {
if (register == null) {
return false;
}
AddressRange regRng = TraceRegisterUtils.rangeForRegister(register);
AddressSpace as = space.getAddressSpace();
AddressRange regRng = coordinates.getPlatform()
.getConventionalRegisterRange(as.isRegisterSpace() ? as : null, register);
return range.getRange().intersects(regRng);
}

View file

@ -21,7 +21,7 @@ import ghidra.app.plugin.core.debug.DebuggerCoordinates;
import ghidra.app.plugin.core.debug.gui.DebuggerResources.TrackLocationAction;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.lang.Register;
import ghidra.trace.model.Trace;
import ghidra.trace.model.guest.TracePlatform;
public class SPLocationTrackingSpec implements RegisterLocationTrackingSpec {
public static final String CONFIG_NAME = "TRACK_SP";
@ -43,11 +43,11 @@ public class SPLocationTrackingSpec implements RegisterLocationTrackingSpec {
@Override
public Register computeRegister(DebuggerCoordinates coordinates) {
Trace trace = coordinates.getTrace();
if (trace == null) {
TracePlatform platform = coordinates.getPlatform();
if (platform == null) {
return null;
}
return trace.getBaseCompilerSpec().getStackPointer();
return platform.getCompilerSpec().getStackPointer();
}
@Override

View file

@ -201,6 +201,7 @@ public class DebuggerListingProvider extends CodeViewerProvider {
@Override
protected void specChanged(LocationTrackingSpec spec) {
updateTitle();
trackingSpecChangeListeners.fire.locationTrackingSpecChanged(spec);
}
@ -1057,6 +1058,7 @@ public class DebuggerListingProvider extends CodeViewerProvider {
trackingTrait.goToCoordinates(coordinates);
readsMemTrait.goToCoordinates(coordinates);
locationLabel.goToCoordinates(coordinates);
updateTitle();
contextChanged();
}

View file

@ -117,6 +117,11 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi
protected void locationTracked() {
doGoToTracked();
}
@Override
protected void specChanged(LocationTrackingSpec spec) {
updateTitle();
}
}
protected class ForMemoryBytesReadsMemoryTrait extends DebuggerReadsMemoryTrait {
@ -382,6 +387,7 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi
trackingTrait.goToCoordinates(coordinates);
readsMemTrait.goToCoordinates(coordinates);
locationLabel.goToCoordinates(coordinates);
updateTitle();
contextChanged();
}