Merge remote-tracking branch

'origin/GP-1451_Dan_syncSelToggle--SQUASHED'

 Conflicts:
	Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingProviderTest.java
This commit is contained in:
Ryan Kurtz 2022-05-20 10:24:35 -04:00
commit a1e57f1f7a
31 changed files with 1063 additions and 421 deletions

View file

@ -128,7 +128,7 @@
</TBODY>
</TABLE>
<H3><A name="sync_static"></A>Sync to Static Listing</H3>
<H3><A name="auto_sync_cursor_static"></A>Auto-Sync Cursor with Static Listing</H3>
<P>This action is always available, but only on the primary dynamic listing. It configures
location synchronization with the (primary) static listing. When enabled, navigation in either
@ -139,7 +139,41 @@
"help/topics/DebuggerStaticMappingPlugin/DebuggerStaticMappingPlugin.html">Static Mappings</A>
window. When you navigate to a location contained by a module, but there is no corresponding
static location, the listing logs a "missing module" to the console, offering either to import
the module or map it to an existing program.</P>
the module or map it to an existing program. If the cursor cannot be mapped, the other
listing's location is left unchanged. If this does not seem correct. Check your module list and
static mappings.</P>
<H3><A name="auto_sync_selection_static"></A>Auto-Sync Selection with Static Listing</H3>
<P>This action is always available, but only on the primary dynamic listing. It configures
selection synchronization with the (primary) static listing. When enabled, selection in either
listing automatically selects the corresponding ranges, if applicable, in the other. In
general, "corresponding ranges" are computed using information about loaded modules reported by
the debugger. For the finer details, see the <A href=
"help/topics/DebuggerStaticMappingPlugin/DebuggerStaticMappingPlugin.html">Static Mappings</A>
window. Portions of the selection which cannot be mapped are omitted.</P>
<H3><A name="sync_selection_into_static"></A>Sync Selection Here into Static Listing</H3>
<P>This action is available whenever the current context is dynamic and has a selection. It
maps the current dynamic selection to corresponding static ranges and selects those in the
static listing. In general, "corresponding ranges" are computed using information about loaded
modules reported by the debugger. For the finer details, see the <A href=
"help/topics/DebuggerStaticMappingPlugin/DebuggerStaticMappingPlugin.html">Static Mappings</A>
window. Portions of the selection which cannot be mapped are omitted. If no part of the
selection is mappable, an error is displayed in the status bar. This can happen if the module
list is missing, or Ghidra could not find the program for the current module.</P>
<H3><A name="sync_selection_from_static"></A>Sync Selection Here from Static Listing</H3>
<P>This action is available whenever the current context is static and has a selection. It maps
the current static selection to corresponding dynamic ranges and selects those in the dynamic
listing. In general, "corresponding ranges" are computed using information about loaded modules
reported by the debugger. For the finer details, see the <A href=
"help/topics/DebuggerStaticMappingPlugin/DebuggerStaticMappingPlugin.html">Static Mappings</A>
window. Portions of the selection which cannot be mapped are omitted. If no part of the
selection is mappable, an error is displayed in the status bar. This can happen if the module
list is missing, or Ghidra could not find the program for the current module.</P>
<H3><A name="open_program"></A>Open Program</H3>

View file

@ -832,20 +832,68 @@ public interface DebuggerResources {
String ownerName = owner.getName();
return new ActionBuilder(NAME, ownerName).description(DESCRIPTION)
.menuPath(NAME)
.menuGroup("a")
.keyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_G, 0))
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
}
}
abstract class AbstractSyncToStaticListingAction extends ToggleDockingAction {
public static final String NAME = "Sync to Static Listing";
public static final String HELP_ANCHOR = "sync_static";
interface AutoSyncCursorWithStaticListingAction {
String NAME = "Auto-Sync Cursor with Static Listing";
String DESCRIPTION = "Automatically synchronize the static and dynamic listings' cursors";
String HELP_ANCHOR = "auto_sync_cursor_static";
public AbstractSyncToStaticListingAction(Plugin owner) {
super(NAME, owner.getName());
setDescription("Synchronize the static listing (and related providers)" +
" to the dynamic listing (and related providers) where a mapping is" + " known");
setHelpLocation(new HelpLocation(owner.getName(), HELP_ANCHOR));
static ToggleActionBuilder builder(Plugin owner) {
String ownerName = owner.getName();
return new ToggleActionBuilder(NAME, ownerName)
.description(DESCRIPTION)
.menuPath(NAME)
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
}
}
interface AutoSyncSelectionWithStaticListingAction {
String NAME = "Auto-Sync Selection with Static Listing";
String DESCRIPTION =
"Automatically synchronize the static and dynamic listings' selections";
String HELP_ANCHOR = "auto_sync_selection_static";
static ToggleActionBuilder builder(Plugin owner) {
String ownerName = owner.getName();
return new ToggleActionBuilder(NAME, ownerName)
.description(DESCRIPTION)
.menuPath(NAME)
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
}
}
interface SyncSelectionIntoStaticListingAction {
String NAME = "Sync Selection into Static Listing";
String DESCRIPTION =
"Change the static listing's selection to synchronize with this component's selection";
String HELP_ANCHOR = "sync_selection_into_static";
static ActionBuilder builder(Plugin owner) {
String ownerName = owner.getName();
return new ActionBuilder(NAME, ownerName)
.description(DESCRIPTION)
.menuPath(NAME)
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
}
}
interface SyncSelectionFromStaticListingAction {
String NAME = "Sync Selection from Static Listing";
String DESCRIPTION =
"Change this component's selection to synchronize with the static listing's selection";
String HELP_ANCHOR = "sync_selection_from_static";
static ActionBuilder builder(Plugin owner) {
String ownerName = owner.getName();
return new ActionBuilder(NAME, ownerName)
.description(DESCRIPTION)
.menuPath(NAME)
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
}
}
@ -883,15 +931,18 @@ public interface DebuggerResources {
}
}
abstract class AbstractFollowsCurrentThreadAction extends ToggleDockingAction {
public static final String NAME = "Follows Selected Thread";
public static final String HELP_ANCHOR = "follows_thread";
interface FollowsCurrentThreadAction {
String NAME = "Follows Selected Thread";
String DESCRIPTION = "Register tracking follows selected thread (and contents" +
" follow selected trace)";
String HELP_ANCHOR = "follows_thread";
public AbstractFollowsCurrentThreadAction(Plugin owner) {
super(NAME, owner.getName());
setDescription("Register tracking follows selected thread (and contents" +
" follow selected trace)");
setHelpLocation(new HelpLocation(owner.getName(), HELP_ANCHOR));
static ToggleActionBuilder builder(Plugin owner) {
String ownerName = owner.getName();
return new ToggleActionBuilder(NAME, ownerName)
.description(DESCRIPTION)
.menuPath(NAME)
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
}
}

View file

@ -0,0 +1,399 @@
/* ###
* 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 java.lang.invoke.MethodHandles;
import java.util.Collection;
import java.util.Set;
import docking.ActionContext;
import docking.ComponentProvider;
import docking.action.DockingAction;
import docking.action.ToggleDockingAction;
import docking.widgets.EventTrigger;
import ghidra.app.context.ProgramLocationActionContext;
import ghidra.app.plugin.core.debug.DebuggerCoordinates;
import ghidra.app.plugin.core.debug.gui.DebuggerResources.*;
import ghidra.app.services.DebuggerStaticMappingChangeListener;
import ghidra.app.services.DebuggerStaticMappingService;
import ghidra.app.services.DebuggerStaticMappingService.MappedAddressRange;
import ghidra.framework.options.SaveState;
import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.annotation.AutoConfigStateField;
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
import ghidra.program.model.address.AddressCollectors;
import ghidra.program.model.address.AddressSet;
import ghidra.program.model.listing.Program;
import ghidra.program.util.ProgramLocation;
import ghidra.program.util.ProgramSelection;
import ghidra.trace.model.Trace;
import ghidra.trace.model.program.TraceProgramView;
import ghidra.util.Msg;
import ghidra.util.Swing;
public class DebuggerStaticSyncTrait {
protected static final AutoConfigState.ClassHandler<DebuggerStaticSyncTrait> CONFIG_STATE_HANDLER =
AutoConfigState.wireHandler(DebuggerStaticSyncTrait.class, MethodHandles.lookup());
private static boolean dynamicHasSelection(ProgramLocationActionContext ctx) {
if (ctx == null) {
return false;
}
ProgramSelection sel = ctx.getSelection();
if (sel == null || sel.isEmpty()) {
return false;
}
return true;
}
protected class ForStaticSyncMappingChangeListener
implements DebuggerStaticMappingChangeListener {
@Override
public void mappingsChanged(Set<Trace> affectedTraces, Set<Program> affectedPrograms) {
Swing.runIfSwingOrRunLater(() -> {
if (current.getView() == null) {
return;
}
if (!affectedTraces.contains(current.getTrace())) {
return;
}
doAutoSyncCursorIntoStatic(currentDynamicLocation);
// TODO: Remember last sync direction, or just always take dyn->static
doAutoSyncSelectionIntoStatic(current.getView(), currentDynamicSelection);
});
/**
* TODO: Remove "missing" entry in modules dialog, if present? There's some nuance here,
* because the trace presenting the mapping may not be the same as the trace that missed
* the module originally. I'm tempted to just leave it and let the user remove it.
*/
}
}
protected ToggleDockingAction actionAutoSyncCursorWithStaticListing;
protected ToggleDockingAction actionAutoSyncSelectionWithStaticListing;
protected DockingAction actionSyncSelectionIntoStaticListing;
protected DockingAction actionSyncSelectionFromStaticListing;
@AutoConfigStateField
private boolean autoSyncCursorWithStaticListing;
@AutoConfigStateField
private boolean autoSyncSelectionWithStaticListing;
private final PluginTool tool;
private final Plugin plugin;
private final ComponentProvider provider;
private final boolean isAutoSyncAllowed;
//@AutoServiceConsumed via method
private DebuggerStaticMappingService mappingService;
@SuppressWarnings("unused")
private final AutoService.Wiring autoServiceWiring;
private DebuggerCoordinates current = DebuggerCoordinates.NOWHERE;
private ProgramLocation currentDynamicLocation;
private ProgramSelection currentDynamicSelection;
private Program currentStaticProgram;
private ProgramLocation currentStaticLocation;
private ProgramSelection currentStaticSelection;
protected final ForStaticSyncMappingChangeListener mappingChangeListener =
new ForStaticSyncMappingChangeListener();
public DebuggerStaticSyncTrait(PluginTool tool, Plugin plugin, ComponentProvider provider,
boolean isAutoSyncAllowed) {
this.tool = tool;
this.plugin = plugin;
this.provider = provider;
this.isAutoSyncAllowed = isAutoSyncAllowed;
this.autoServiceWiring = AutoService.wireServicesConsumed(plugin, this);
this.autoSyncCursorWithStaticListing = isAutoSyncAllowed;
this.autoSyncSelectionWithStaticListing = isAutoSyncAllowed;
}
@AutoServiceConsumed
private void setMappingService(DebuggerStaticMappingService mappingService) {
if (this.mappingService != null) {
this.mappingService.removeChangeListener(mappingChangeListener);
}
this.mappingService = mappingService;
if (this.mappingService != null) {
this.mappingService.addChangeListener(mappingChangeListener);
doAutoSyncCursorIntoStatic(currentDynamicLocation);
}
}
public ToggleDockingAction installAutoSyncCursorWithStaticListingAction() {
return actionAutoSyncCursorWithStaticListing = AutoSyncCursorWithStaticListingAction
.builder(plugin)
.enabled(true)
.selected(true)
.onAction(ctx -> doSetAutoSyncCursorWithStaticListing(
actionAutoSyncCursorWithStaticListing.isSelected()))
.buildAndInstallLocal(provider);
}
public ToggleDockingAction installAutoSyncSelectionWithStaticListingAction() {
return actionAutoSyncSelectionWithStaticListing = AutoSyncSelectionWithStaticListingAction
.builder(plugin)
.enabled(true)
.selected(true)
.onAction(ctx -> doSetAutoSyncSelectionWithStaticListing(
actionAutoSyncSelectionWithStaticListing.isSelected()))
.buildAndInstallLocal(provider);
}
public DockingAction installSyncSelectionIntoStaticListingAction() {
return actionSyncSelectionIntoStaticListing = SyncSelectionIntoStaticListingAction
.builder(plugin)
.withContext(ProgramLocationActionContext.class)
.enabledWhen(ctx -> dynamicHasSelection(ctx))
.onAction(this::activatedSyncSelectionIntoStatic)
.buildAndInstallLocal(provider);
}
public DockingAction installSyncSelectionFromStaticListingAction() {
return actionSyncSelectionFromStaticListing = SyncSelectionFromStaticListingAction
.builder(plugin)
.withContext(ProgramLocationActionContext.class)
.enabledWhen(ctx -> staticHasSelection(ctx))
.onAction(this::activatedSyncSelectionFromStatic)
.buildAndInstallLocal(provider);
}
private boolean staticHasSelection(ActionContext ctx) {
return currentStaticSelection != null && !currentStaticSelection.isEmpty();
}
protected void activatedSyncSelectionIntoStatic(ProgramLocationActionContext ctx) {
ProgramSelection result = doSyncSelectionIntoStatic(ctx.getProgram(), ctx.getSelection());
if (result != null && result.isEmpty()) {
displayMapError("the dynamic view", "the static listing");
}
}
protected void activatedSyncSelectionFromStatic(ActionContext ctx) {
ProgramSelection result = doSyncSelectionFromStatic();
if (result != null && result.isEmpty()) {
displayMapError("the static listing", "the dynamic view");
}
}
protected void doSyncCursorIntoStatic(ProgramLocation location) {
if (location == null) {
return;
}
ProgramLocation staticLoc = mappingService.getStaticLocationFromDynamic(location);
if (staticLoc == null) {
return;
}
staticGoTo(staticLoc);
}
protected void doSyncCursorFromStatic() {
TraceProgramView view = current.getView(); // NB. Used for snap (don't want emuSnap)
if (view == null || currentStaticLocation == null) {
return;
}
ProgramLocation dynamicLoc =
mappingService.getDynamicLocationFromStatic(view, currentStaticLocation);
if (dynamicLoc == null) {
return;
}
dynamicGoTo(dynamicLoc);
}
public void doAutoSyncCursorIntoStatic(ProgramLocation location) {
if (!isAutoSyncCursorWithStaticListing()) {
return;
}
doSyncCursorIntoStatic(location);
}
public void doAutoSyncCursorFromStatic() {
if (!isAutoSyncCursorWithStaticListing()) {
return;
}
doSyncCursorFromStatic();
}
protected void doSetAutoSyncCursorWithStaticListing(boolean sync) {
this.autoSyncCursorWithStaticListing = sync;
provider.contextChanged();
doAutoSyncCursorIntoStatic(currentDynamicLocation);
}
protected void doSetAutoSyncSelectionWithStaticListing(boolean sync) {
this.autoSyncSelectionWithStaticListing = sync;
provider.contextChanged();
doAutoSyncSelectionIntoStatic(current.getView(), currentDynamicSelection);
}
protected ProgramSelection doSyncSelectionIntoStatic(Program program, ProgramSelection sel) {
if (program == null || sel == null || currentStaticProgram == null) {
return null;
}
TraceProgramView view = (TraceProgramView) program;
Collection<MappedAddressRange> ranges =
mappingService.getOpenMappedViews(view.getTrace(), sel, view.getSnap())
.get(currentStaticProgram);
AddressSet mapped;
if (ranges == null) {
mapped = new AddressSet();
}
else {
mapped = ranges.stream()
.map(r -> r.getDestinationAddressRange())
.collect(AddressCollectors.toAddressSet());
}
ProgramSelection result = new ProgramSelection(mapped);
staticSelect(currentStaticProgram, result);
return result;
}
protected ProgramSelection doSyncSelectionFromStatic() {
TraceProgramView view = current.getView();
if (view == null || currentStaticProgram == null || currentStaticSelection == null) {
return null;
}
AddressSet mapped =
mappingService.getOpenMappedViews(currentStaticProgram, currentStaticSelection)
.entrySet()
.stream()
.filter(e -> e.getKey().getTrace() == current.getTrace())
.filter(e -> e.getKey().getSpan().contains(current.getSnap()))
.flatMap(e -> e.getValue().stream())
.map(r -> r.getDestinationAddressRange())
.collect(AddressCollectors.toAddressSet());
ProgramSelection result = new ProgramSelection(mapped);
dynamicSelect(view, result);
return result;
}
protected void doAutoSyncSelectionIntoStatic(Program program, ProgramSelection selection) {
if (isAutoSyncSelectionWithStaticListing()) {
doSyncSelectionIntoStatic(program, selection);
}
}
protected void doAutoSyncSelectionFromStatic() {
if (isAutoSyncSelectionWithStaticListing()) {
doSyncSelectionFromStatic();
}
}
protected void displayMapError(String from, String to) {
tool.setStatusInfo("No selected addresses in " + from + " are mappable to " + to +
". Check your module list and static mappings.", true);
}
public void goToCoordinates(DebuggerCoordinates coordinates) {
this.current = coordinates;
}
public void dynamicProgramLocationChanged(ProgramLocation location, EventTrigger trigger) {
currentDynamicLocation = location;
if (trigger != EventTrigger.GUI_ACTION) {
return;
}
doAutoSyncCursorIntoStatic(location);
}
public void dynamicSelectionChanged(Program program, ProgramSelection selection,
EventTrigger trigger) {
currentDynamicSelection = selection;
provider.contextChanged();
if (trigger != EventTrigger.GUI_ACTION) {
return;
}
doAutoSyncSelectionIntoStatic(program, selection);
}
public void staticProgramActivated(Program program) {
currentStaticProgram = program;
}
public void staticProgramLocationChanged(ProgramLocation location) {
currentStaticLocation = location;
doAutoSyncCursorFromStatic();
}
public void staticProgramSelectionChanged(Program program, ProgramSelection selection) {
if (program != currentStaticProgram) {
Msg.warn(this, "Got selection change for not the current static program");
return;
}
currentStaticProgram = program;
currentStaticSelection = selection;
provider.contextChanged();
doAutoSyncSelectionFromStatic();
}
public void setAutoSyncCursorWithStaticListing(boolean sync) {
actionAutoSyncCursorWithStaticListing.setSelected(sync);
doSetAutoSyncCursorWithStaticListing(sync);
}
public boolean isAutoSyncCursorWithStaticListing() {
return autoSyncCursorWithStaticListing;
}
public void setAutoSyncSelectionWithStaticListing(boolean sync) {
actionAutoSyncSelectionWithStaticListing.setSelected(sync);
doSetAutoSyncSelectionWithStaticListing(sync);
}
public boolean isAutoSyncSelectionWithStaticListing() {
return autoSyncSelectionWithStaticListing;
}
public void readConfigState(SaveState saveState) {
CONFIG_STATE_HANDLER.readConfigState(this, saveState);
if (isAutoSyncAllowed) {
if (actionAutoSyncCursorWithStaticListing != null) {
actionAutoSyncCursorWithStaticListing.setSelected(autoSyncCursorWithStaticListing);
}
if (actionAutoSyncSelectionWithStaticListing != null) {
actionAutoSyncSelectionWithStaticListing
.setSelected(autoSyncSelectionWithStaticListing);
}
}
else {
autoSyncCursorWithStaticListing = false;
autoSyncSelectionWithStaticListing = false;
}
}
protected void staticGoTo(ProgramLocation location) {
// listener method
}
protected void staticSelect(Program program, ProgramSelection selection) {
// listener method
}
protected void dynamicGoTo(ProgramLocation location) {
// listener method
}
protected void dynamicSelect(Program program, ProgramSelection selection) {
// listener method
}
}

View file

@ -47,6 +47,7 @@ import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
import ghidra.framework.plugintool.util.PluginStatus;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.program.util.ProgramLocation;
import ghidra.program.util.ProgramSelection;
import ghidra.trace.model.program.TraceProgramView;
@ -63,17 +64,18 @@ import utilities.util.SuppressableCallback.Suppression;
packageName = DebuggerPluginPackage.NAME,
status = PluginStatus.RELEASED,
eventsConsumed = {
// ProgramSelectionPluginEvent.class, // TODO: Later or remove
// ProgramHighlightPluginEvent.class, // TODO: Later or remove
ProgramOpenedPluginEvent.class, // For auto-open log cleanup
ProgramClosedPluginEvent.class, // For marker set cleanup
ProgramActivatedPluginEvent.class, // To track the static program for sync
ProgramLocationPluginEvent.class, // For static listing sync
ProgramSelectionPluginEvent.class, // For static listing sync
TraceActivatedPluginEvent.class, // Trace/thread activation and register tracking
TraceClosedPluginEvent.class,
},
eventsProduced = {
ProgramLocationPluginEvent.class,
// ProgramSelectionPluginEvent.class,
ProgramSelectionPluginEvent.class,
TraceLocationPluginEvent.class,
TraceSelectionPluginEvent.class
},
@ -150,8 +152,9 @@ public class DebuggerListingPlugin extends AbstractCodeBrowserPlugin<DebuggerLis
@SuppressWarnings("unused")
private AutoOptions.Wiring autoOptionsWiring;
//private final SuppressableCallback<Void> cbGoTo = new SuppressableCallback<>();
private final SuppressableCallback<Void> cbProgramLocationEvents = new SuppressableCallback<>();
private final SuppressableCallback<Void> cbProgramSelectionEvents =
new SuppressableCallback<>();
private DebuggerCoordinates current = DebuggerCoordinates.NOWHERE;
@ -242,7 +245,7 @@ public class DebuggerListingPlugin extends AbstractCodeBrowserPlugin<DebuggerLis
// TODO Nothing, yet
}
protected boolean heedLocationEvent(ProgramLocationPluginEvent ev) {
protected boolean heedLocationEvent(PluginEvent ev) {
PluginEvent trigger = ev.getTriggerEvent();
/*Msg.debug(this, "Location event");
Msg.debug(this, " Program: " + ev.getProgram());
@ -268,6 +271,10 @@ public class DebuggerListingPlugin extends AbstractCodeBrowserPlugin<DebuggerLis
return true;
}
protected boolean heedSelectionEvent(PluginEvent ev) {
return heedLocationEvent(ev);
}
@Override
public void processEvent(PluginEvent event) {
if (event instanceof ProgramLocationPluginEvent) {
@ -278,6 +285,15 @@ public class DebuggerListingPlugin extends AbstractCodeBrowserPlugin<DebuggerLis
}
});
}
if (event instanceof ProgramSelectionPluginEvent) {
cbProgramSelectionEvents.invoke(() -> {
ProgramSelectionPluginEvent ev = (ProgramSelectionPluginEvent) event;
if (heedSelectionEvent(ev)) {
connectedProvider.staticProgramSelectionChanged(ev.getProgram(),
ev.getSelection());
}
});
}
if (event instanceof ProgramOpenedPluginEvent) {
ProgramOpenedPluginEvent ev = (ProgramOpenedPluginEvent) event;
allProviders(p -> p.programOpened(ev.getProgram()));
@ -286,6 +302,10 @@ public class DebuggerListingPlugin extends AbstractCodeBrowserPlugin<DebuggerLis
ProgramClosedPluginEvent ev = (ProgramClosedPluginEvent) event;
allProviders(p -> p.programClosed(ev.getProgram()));
}
if (event instanceof ProgramActivatedPluginEvent) {
ProgramActivatedPluginEvent ev = (ProgramActivatedPluginEvent) event;
allProviders(p -> p.staticProgramActivated(ev.getActiveProgram()));
}
if (event instanceof TraceActivatedPluginEvent) {
TraceActivatedPluginEvent ev = (TraceActivatedPluginEvent) event;
current = ev.getActiveCoordinates();
@ -312,6 +332,13 @@ public class DebuggerListingPlugin extends AbstractCodeBrowserPlugin<DebuggerLis
}
}
void fireStaticSelectionEvent(Program staticProg, ProgramSelection staticSel) {
assert Swing.isSwingThread();
try (Suppression supp = cbProgramSelectionEvents.suppress(null)) {
tool.firePluginEvent(new ProgramSelectionPluginEvent(getName(), staticSel, staticProg));
}
}
protected void allProviders(Consumer<DebuggerListingProvider> action) {
action.accept(connectedProvider);
for (DebuggerListingProvider provider : disconnectedProviders) {
@ -359,11 +386,9 @@ public class DebuggerListingPlugin extends AbstractCodeBrowserPlugin<DebuggerLis
if (!result) {
return false;
}
//cbGoTo.invoke(() -> {
DebuggerListingProvider provider = connectedProvider;
provider.doSyncToStatic(location);
provider.doAutoSyncCursorIntoStatic(location);
provider.doCheckCurrentModuleMissing();
//});
return true;
}

View file

@ -30,10 +30,9 @@ import javax.swing.event.ChangeListener;
import org.apache.commons.lang3.StringUtils;
import org.jdom.Element;
import docking.ActionContext;
import docking.WindowPosition;
import docking.action.DockingAction;
import docking.action.MenuData;
import docking.action.ToggleDockingAction;
import docking.menu.MultiStateDockingAction;
import docking.widgets.EventTrigger;
import docking.widgets.fieldpanel.support.ViewerPosition;
@ -43,7 +42,8 @@ import ghidra.app.plugin.core.codebrowser.MarkerServiceBackgroundColorModel;
import ghidra.app.plugin.core.debug.DebuggerCoordinates;
import ghidra.app.plugin.core.debug.gui.DebuggerLocationLabel;
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
import ghidra.app.plugin.core.debug.gui.DebuggerResources.*;
import ghidra.app.plugin.core.debug.gui.DebuggerResources.FollowsCurrentThreadAction;
import ghidra.app.plugin.core.debug.gui.DebuggerResources.OpenProgramAction;
import ghidra.app.plugin.core.debug.gui.action.*;
import ghidra.app.plugin.core.debug.gui.modules.DebuggerMissingModuleActionContext;
import ghidra.app.plugin.core.debug.utils.ProgramLocationUtils;
@ -105,36 +105,6 @@ public class DebuggerListingProvider extends CodeViewerProvider {
return true;
}
protected class SyncToStaticListingAction extends AbstractSyncToStaticListingAction {
public SyncToStaticListingAction() {
super(plugin);
setMenuBarData(new MenuData(new String[] { getName() }));
setSelected(true);
addLocalAction(this);
setEnabled(true);
}
@Override
public void actionPerformed(ActionContext context) {
doSetSyncToStaticListing(isSelected());
}
}
protected class FollowsCurrentThreadAction extends AbstractFollowsCurrentThreadAction {
public FollowsCurrentThreadAction() {
super(plugin);
setMenuBarData(new MenuData(new String[] { NAME }));
setSelected(true);
addLocalAction(this);
setEnabled(true);
}
@Override
public void actionPerformed(ActionContext context) {
doSetFollowsCurrentThread(isSelected());
}
}
protected class MarkerSetChangeListener implements ChangeListener {
@Override
public void stateChanged(ChangeEvent e) {
@ -154,7 +124,6 @@ public class DebuggerListingProvider extends CodeViewerProvider {
return;
}
doMarkTrackedLocation();
doSyncToStatic(getLocation());
});
/**
@ -165,6 +134,53 @@ public class DebuggerListingProvider extends CodeViewerProvider {
}
}
protected class ForListingSyncTrait extends DebuggerStaticSyncTrait {
public ForListingSyncTrait() {
super(DebuggerListingProvider.this.tool, DebuggerListingProvider.this.plugin,
DebuggerListingProvider.this, isMainListing());
}
@Override
protected void staticGoTo(ProgramLocation location) {
Swing.runIfSwingOrRunLater(() -> plugin.fireStaticLocationEvent(location));
}
@Override
protected void staticSelect(Program program, ProgramSelection selection) {
Swing.runIfSwingOrRunLater(() -> plugin.fireStaticSelectionEvent(program, selection));
if (selection.isEmpty()) {
return;
}
Optional<CodeViewerService> codeViewer =
Stream.of(tool.getServices(CodeViewerService.class))
.filter(cv -> cv != plugin)
.findFirst();
if (codeViewer.isEmpty()) {
return;
}
Swing.runIfSwingOrRunLater(
() -> codeViewer.get()
.getListingPanel()
.scrollTo(new ProgramLocation(program, selection.getMinAddress())));
}
@Override
protected void dynamicGoTo(ProgramLocation location) {
Swing.runIfSwingOrRunLater(() -> goTo(location.getProgram(), location));
}
@Override
protected void dynamicSelect(Program program, ProgramSelection selection) {
Swing.runIfSwingOrRunLater(() -> {
setSelection(selection);
if (!selection.isEmpty()) {
getListingPanel()
.scrollTo(new ProgramLocation(program, selection.getMinAddress()));
}
});
}
}
protected class ForListingGoToTrait extends DebuggerGoToTrait {
public ForListingGoToTrait() {
super(DebuggerListingProvider.this.tool, DebuggerListingProvider.this.plugin,
@ -242,20 +258,22 @@ public class DebuggerListingProvider extends CodeViewerProvider {
protected MarkerSet trackingMarker;
protected DockingAction actionGoTo;
protected SyncToStaticListingAction actionSyncToStaticListing;
protected FollowsCurrentThreadAction actionFollowsCurrentThread;
protected ToggleDockingAction actionAutoSyncCursorWithStaticListing;
protected ToggleDockingAction actionAutoSyncSelectionWithStaticListing;
protected DockingAction actionSyncSelectionIntoStaticListing;
protected DockingAction actionSyncSelectionFromStaticListing;
protected ToggleDockingAction actionFollowsCurrentThread;
protected MultiStateDockingAction<AutoReadMemorySpec> actionAutoReadMemory;
protected DockingAction actionRefreshSelectedMemory;
protected DockingAction actionOpenProgram;
protected MultiStateDockingAction<LocationTrackingSpec> actionTrackLocation;
@AutoConfigStateField
protected boolean syncToStaticListing;
@AutoConfigStateField
protected boolean followsCurrentThread = true;
// TODO: followsCurrentSnap?
protected final DebuggerGoToTrait goToTrait;
protected final ForListingSyncTrait syncTrait;
protected final ForListingGoToTrait goToTrait;
protected final ForListingTrackingTrait trackingTrait;
protected final ForListingReadsMemoryTrait readsMemTrait;
@ -283,6 +301,7 @@ public class DebuggerListingProvider extends CodeViewerProvider {
this.plugin = plugin;
this.isMainListing = isConnected;
syncTrait = new ForListingSyncTrait();
goToTrait = new ForListingGoToTrait();
trackingTrait = new ForListingTrackingTrait();
readsMemTrait = new ForListingReadsMemoryTrait();
@ -295,7 +314,6 @@ public class DebuggerListingProvider extends CodeViewerProvider {
autoServiceWiring = AutoService.wireServicesConsumed(plugin, this);
autoOptionsWiring = AutoOptions.wireOptionsConsumed(plugin, this);
syncToStaticListing = isConnected;
setVisible(true);
createActions();
@ -306,7 +324,6 @@ public class DebuggerListingProvider extends CodeViewerProvider {
// TODO: An icon to distinguish dynamic from static
//getComponent().setBorder(BorderFactory.createEmptyBorder());
addDisplayListener(readsMemTrait.getDisplayListener());
this.setNorthComponent(locationLabel);
@ -412,15 +429,14 @@ public class DebuggerListingProvider extends CodeViewerProvider {
}
CONFIG_STATE_HANDLER.readConfigState(this, saveState);
syncTrait.readConfigState(saveState);
trackingTrait.readConfigState(saveState);
readsMemTrait.readConfigState(saveState);
if (isMainListing()) {
actionSyncToStaticListing.setSelected(syncToStaticListing);
followsCurrentThread = true;
}
else {
syncToStaticListing = false;
actionFollowsCurrentThread.setSelected(followsCurrentThread);
updateBorder();
}
@ -458,7 +474,6 @@ public class DebuggerListingProvider extends CodeViewerProvider {
if (this.mappingService != null) {
this.mappingService.addChangeListener(mappingChangeListener);
doMarkTrackedLocation();
doSyncToStatic(getLocation());
}
}
@ -560,6 +575,10 @@ public class DebuggerListingProvider extends CodeViewerProvider {
}
}
public void staticProgramActivated(Program program) {
syncTrait.staticProgramActivated(program);
}
@Override
protected void doSetProgram(Program newProgram) {
if (newProgram != null && newProgram != current.getView()) {
@ -633,12 +652,25 @@ public class DebuggerListingProvider extends CodeViewerProvider {
protected void createActions() {
if (isMainListing()) {
actionSyncToStaticListing = new SyncToStaticListingAction();
actionAutoSyncCursorWithStaticListing =
syncTrait.installAutoSyncCursorWithStaticListingAction();
actionAutoSyncSelectionWithStaticListing =
syncTrait.installAutoSyncSelectionWithStaticListingAction();
}
else {
actionFollowsCurrentThread = new FollowsCurrentThreadAction();
actionFollowsCurrentThread = FollowsCurrentThreadAction.builder(plugin)
.enabled(true)
.selected(true)
.onAction(
ctx -> doSetFollowsCurrentThread(actionFollowsCurrentThread.isSelected()))
.buildAndInstallLocal(this);
}
actionSyncSelectionIntoStaticListing =
syncTrait.installSyncSelectionIntoStaticListingAction();
actionSyncSelectionFromStaticListing =
syncTrait.installSyncSelectionFromStaticListingAction();
actionGoTo = goToTrait.installAction();
actionTrackLocation = trackingTrait.installAction();
actionAutoReadMemory = readsMemTrait.installAutoReadAction();
@ -717,8 +749,6 @@ public class DebuggerListingProvider extends CodeViewerProvider {
return false;
}
if (super.goTo(gotoProgram, location)) {
//doSyncToStatic(location);
//doAutoImportCurrentModule();
return true;
}
return false;
@ -733,19 +763,16 @@ public class DebuggerListingProvider extends CodeViewerProvider {
location = ProgramLocationUtils.fixLocation(location, false);
}
super.programLocationChanged(location, trigger);
syncTrait.dynamicProgramLocationChanged(location, trigger);
if (trigger == EventTrigger.GUI_ACTION) {
doSyncToStatic(location);
doCheckCurrentModuleMissing();
}
}
protected void doSyncToStatic(ProgramLocation location) {
if (isSyncToStaticListing() && location != null) {
ProgramLocation staticLoc = mappingService.getStaticLocationFromDynamic(location);
if (staticLoc != null) {
Swing.runIfSwingOrRunLater(() -> plugin.fireStaticLocationEvent(staticLoc));
}
}
@Override
public void programSelectionChanged(ProgramSelection selection, EventTrigger trigger) {
super.programSelectionChanged(selection, trigger);
syncTrait.dynamicSelectionChanged(getProgram(), selection, trigger);
}
protected void doTryOpenProgram(DomainFile df, int version, int state) {
@ -799,7 +826,7 @@ public class DebuggerListingProvider extends CodeViewerProvider {
protected void doCheckCurrentModuleMissing() {
// Is there any reason to try to open the module if we're not syncing listings?
// I don't think so.
if (!isSyncToStaticListing()) {
if (!syncTrait.isAutoSyncCursorWithStaticListing()) {
return;
}
Trace trace = current.getTrace();
@ -890,23 +917,20 @@ public class DebuggerListingProvider extends CodeViewerProvider {
trackingSpecChangeListeners.remove(listener);
}
public void setSyncToStaticListing(boolean sync) {
public void setAutoSyncCursorWithStaticListing(boolean sync) {
if (!isMainListing()) {
throw new IllegalStateException(
"Only the main dynamic listing can be synced to the main static listing");
}
actionSyncToStaticListing.setSelected(sync);
doSetSyncToStaticListing(sync);
syncTrait.setAutoSyncCursorWithStaticListing(sync);
}
protected void doSetSyncToStaticListing(boolean sync) {
this.syncToStaticListing = sync;
contextChanged();
doSyncToStatic(getLocation());
}
public boolean isSyncToStaticListing() {
return syncToStaticListing;
public void setAutoSyncSelectionWithStaticListing(boolean sync) {
if (!isMainListing()) {
throw new IllegalStateException(
"Only the main dynamic listing can be synced to the main static listing");
}
syncTrait.setAutoSyncSelectionWithStaticListing(sync);
}
public void setFollowsCurrentThread(boolean follows) {
@ -943,6 +967,10 @@ public class DebuggerListingProvider extends CodeViewerProvider {
return readsMemTrait.getAutoSpec();
}
public void doAutoSyncCursorIntoStatic(ProgramLocation location) {
syncTrait.doAutoSyncCursorIntoStatic(location);
}
protected ProgramLocation doMarkTrackedLocation() {
ProgramLocation trackedLocation = trackingTrait.getTrackedLocation();
if (trackedLocation == null) {
@ -962,7 +990,7 @@ public class DebuggerListingProvider extends CodeViewerProvider {
return;
}
TraceProgramView curView = current.getView();
if (!syncToStaticListing || trackedStatic == null) {
if (!syncTrait.isAutoSyncCursorWithStaticListing() || trackedStatic == null) {
Swing.runIfSwingOrRunLater(() -> {
goTo(curView, loc);
doCheckCurrentModuleMissing();
@ -987,18 +1015,6 @@ public class DebuggerListingProvider extends CodeViewerProvider {
}
}
public void staticProgramLocationChanged(ProgramLocation location) {
TraceProgramView view = current.getView(); // NB. Used for snap (don't want emuSnap)
if (!isSyncToStaticListing() || view == null || location == null) {
return;
}
ProgramLocation dyn = mappingService.getDynamicLocationFromStatic(view, location);
if (dyn == null) {
return;
}
goTo(view, dyn);
}
protected DebuggerCoordinates adjustCoordinates(DebuggerCoordinates coordinates) {
if (followsCurrentThread) {
return coordinates;
@ -1014,6 +1030,7 @@ public class DebuggerListingProvider extends CodeViewerProvider {
}
current = coordinates;
doSetProgram(current.getView());
syncTrait.goToCoordinates(coordinates);
goToTrait.goToCoordinates(coordinates);
trackingTrait.goToCoordinates(coordinates);
readsMemTrait.goToCoordinates(coordinates);
@ -1032,6 +1049,14 @@ public class DebuggerListingProvider extends CodeViewerProvider {
}
}
public void staticProgramLocationChanged(ProgramLocation location) {
syncTrait.staticProgramLocationChanged(location);
}
public void staticProgramSelectionChanged(Program program, ProgramSelection selection) {
syncTrait.staticProgramSelectionChanged(program, selection);
}
@Override
public void cloneWindow() {
final DebuggerListingProvider newProvider = plugin.createNewDisconnectedProvider();

View file

@ -22,16 +22,15 @@ import java.util.*;
import org.apache.commons.lang3.StringUtils;
import docking.ActionContext;
import docking.action.DockingAction;
import docking.action.MenuData;
import docking.action.ToggleDockingAction;
import docking.menu.MultiStateDockingAction;
import docking.widgets.fieldpanel.support.ViewerPosition;
import ghidra.app.plugin.core.byteviewer.*;
import ghidra.app.plugin.core.debug.DebuggerCoordinates;
import ghidra.app.plugin.core.debug.gui.DebuggerLocationLabel;
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
import ghidra.app.plugin.core.debug.gui.DebuggerResources.AbstractFollowsCurrentThreadAction;
import ghidra.app.plugin.core.debug.gui.DebuggerResources.FollowsCurrentThreadAction;
import ghidra.app.plugin.core.debug.gui.action.*;
import ghidra.app.plugin.core.debug.gui.action.AutoReadMemorySpec.AutoReadMemorySpecConfigFieldCodec;
import ghidra.app.plugin.core.format.ByteBlock;
@ -76,21 +75,6 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi
return true;
}
protected class FollowsCurrentThreadAction extends AbstractFollowsCurrentThreadAction {
public FollowsCurrentThreadAction() {
super(plugin);
setMenuBarData(new MenuData(new String[] { NAME }));
setSelected(true);
addLocalAction(this);
setEnabled(true);
}
@Override
public void actionPerformed(ActionContext context) {
doSetFollowsCurrentThread(isSelected());
}
}
protected class ForMemoryBytesGoToTrait extends DebuggerGoToTrait {
public ForMemoryBytesGoToTrait() {
super(DebuggerMemoryBytesProvider.this.tool, DebuggerMemoryBytesProvider.this.plugin,
@ -150,7 +134,7 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi
private final AutoService.Wiring autoServiceWiring;
protected DockingAction actionGoTo;
protected FollowsCurrentThreadAction actionFollowsCurrentThread;
protected ToggleDockingAction actionFollowsCurrentThread;
protected MultiStateDockingAction<AutoReadMemorySpec> actionAutoReadMemory;
protected DockingAction actionRefreshSelectedMemory;
protected MultiStateDockingAction<LocationTrackingSpec> actionTrackLocation;
@ -280,7 +264,12 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi
initTraits();
if (!isMainViewer()) {
actionFollowsCurrentThread = new FollowsCurrentThreadAction();
actionFollowsCurrentThread = FollowsCurrentThreadAction.builder(plugin)
.enabled(true)
.selected(true)
.onAction(
ctx -> doSetFollowsCurrentThread(actionFollowsCurrentThread.isSelected()))
.buildAndInstallLocal(this);
}
actionGoTo = goToTrait.installAction();

View file

@ -18,9 +18,9 @@ package ghidra.app.plugin.core.debug.gui.watch;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Predicate;
@ -44,7 +44,6 @@ import ghidra.app.plugin.core.debug.gui.DebuggerResources.*;
import ghidra.app.plugin.core.debug.gui.register.DebuggerRegisterActionContext;
import ghidra.app.plugin.core.debug.gui.register.RegisterRow;
import ghidra.app.services.*;
import ghidra.app.services.DebuggerStaticMappingService.MappedAddressRange;
import ghidra.async.AsyncDebouncer;
import ghidra.async.AsyncTimer;
import ghidra.base.widgets.table.DataTypeTableCellEditor;
@ -603,21 +602,14 @@ public class DebuggerWatchesProvider extends ComponentProviderAdapter {
if (set == null) {
return null;
}
AddressSet result = new AddressSet();
for (Entry<TraceSpan, Collection<MappedAddressRange>> ent : mappingService
.getOpenMappedViews(program, set)
.entrySet()) {
if (ent.getKey().getTrace() != current.getTrace()) {
continue;
}
if (!ent.getKey().getSpan().contains(current.getSnap())) {
continue;
}
for (MappedAddressRange rng : ent.getValue()) {
result.add(rng.getDestinationAddressRange());
}
}
return result;
return mappingService.getOpenMappedViews(program, set)
.entrySet()
.stream()
.filter(e -> e.getKey().getTrace() == current.getTrace())
.filter(e -> e.getKey().getSpan().contains(current.getSnap()))
.flatMap(e -> e.getValue().stream())
.map(r -> r.getDestinationAddressRange())
.collect(AddressCollectors.toAddressSet());
}
private boolean hasDynamicLocation(ProgramLocationActionContext context) {

View file

@ -18,8 +18,6 @@ package ghidra.app.plugin.core.debug.service.model.record;
import java.nio.ByteBuffer;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.function.*;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import ghidra.dbg.error.DebuggerMemoryAccessException;
@ -137,43 +135,11 @@ class MemoryRecorder {
return true;
}
protected Collector<AddressRange, AddressSet, AddressSet> toAddressSet() {
return new Collector<>() {
@Override
public Supplier<AddressSet> supplier() {
return AddressSet::new;
}
@Override
public BiConsumer<AddressSet, AddressRange> accumulator() {
return AddressSet::add;
}
@Override
public BinaryOperator<AddressSet> combiner() {
return (s1, s2) -> {
s1.add(s2);
return s1;
};
}
@Override
public Function<AddressSet, AddressSet> finisher() {
return Function.identity();
}
@Override
public Set<Characteristics> characteristics() {
return Set.of();
}
};
}
public AddressSetView getAccessible() {
synchronized (regions) {
return regions.values()
.stream()
.collect(toAddressSet());
.collect(AddressCollectors.toAddressSet());
}
}
}

View file

@ -926,7 +926,7 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
synchronized (lock) {
InfoPerProgram info = requireTrackedInfo(program);
if (info == null) {
return null;
return Map.of();
}
return info.getOpenMappedViews(set);
}

View file

@ -15,7 +15,6 @@
*/
package ghidra.app.plugin.core.debug.gui.listing;
import static ghidra.lifecycle.Unfinished.TODO;
import static org.junit.Assert.*;
import java.awt.Color;
@ -34,10 +33,12 @@ import docking.menu.ActionState;
import docking.menu.MultiStateDockingAction;
import docking.widgets.EventTrigger;
import generic.test.category.NightlyCategory;
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
import ghidra.app.plugin.core.codebrowser.CodeViewerProvider;
import ghidra.app.plugin.core.debug.DebuggerCoordinates;
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest;
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
import ghidra.app.plugin.core.debug.gui.DebuggerResources.AbstractFollowsCurrentThreadAction;
import ghidra.app.plugin.core.debug.gui.DebuggerResources.FollowsCurrentThreadAction;
import ghidra.app.plugin.core.debug.gui.action.AutoReadMemorySpec;
import ghidra.app.plugin.core.debug.gui.action.DebuggerGoToDialog;
import ghidra.app.plugin.core.debug.gui.console.DebuggerConsolePlugin;
@ -48,6 +49,7 @@ import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils;
import ghidra.app.services.*;
import ghidra.async.SwingExecutorService;
import ghidra.framework.model.*;
import ghidra.lifecycle.Unfinished;
import ghidra.plugin.importer.ImporterPlugin;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.Register;
@ -75,15 +77,19 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
protected DebuggerListingProvider listingProvider;
protected DebuggerStaticMappingService mappingService;
protected CodeViewerService codeViewer;
protected CodeBrowserPlugin codePlugin;
protected CodeViewerProvider codeProvider;
@Before
public void setUpListingProviderTest() throws Exception {
// Do before listingPlugin, since types collide
codePlugin = addPlugin(tool, CodeBrowserPlugin.class);
codeProvider = waitForComponentProvider(CodeViewerProvider.class);
listingPlugin = addPlugin(tool, DebuggerListingPlugin.class);
listingProvider = waitForComponentProvider(DebuggerListingProvider.class);
mappingService = tool.getService(DebuggerStaticMappingService.class);
codeViewer = tool.getService(CodeViewerService.class);
}
protected void goToDyn(Address address) {
@ -102,6 +108,33 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
return data;
}
protected void createMappedTraceAndProgram() throws Exception {
createAndOpenTrace();
createAndOpenProgramFromTrace();
intoProject(tb.trace);
intoProject(program);
AddressSpace ss = program.getAddressFactory().getDefaultAddressSpace();
try (UndoableTransaction tid = UndoableTransaction.start(program, "Add block", true)) {
program.getMemory()
.createInitializedBlock(".text", ss.getAddress(0x00600000), 0x10000, (byte) 0,
monitor, false);
}
try (UndoableTransaction tid = tb.startTransaction()) {
DBTraceMemoryManager memory = tb.trace.getMemoryManager();
memory.addRegion("exe:.text", Range.atLeast(0L), tb.range(0x00400000, 0x0040ffff),
TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
TraceLocation from =
new DefaultTraceLocation(tb.trace, null, Range.atLeast(0L), tb.addr(0x00400000));
ProgramLocation to = new ProgramLocation(program, ss.getAddress(0x00600000));
DebuggerStaticMappingUtils.addMapping(from, to, 0x8000, false);
}
waitForProgram(program);
waitForDomainObject(tb.trace);
traceManager.activateTrace(tb.trace);
waitForSwing();
}
@Test
public void testListingViewIsRegionsActivateThenAdd() throws Exception {
createAndOpenTrace();
@ -419,31 +452,10 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
}
@Test
public void testSyncToStaticListingStaticToDynamicOnGoto() throws Exception {
createAndOpenTrace();
createAndOpenProgramFromTrace();
intoProject(tb.trace);
intoProject(program);
public void testSyncCursorToStaticListingStaticToDynamicOnGoto() throws Exception {
createMappedTraceAndProgram();
AddressSpace ss = program.getAddressFactory().getDefaultAddressSpace();
try (UndoableTransaction tid = UndoableTransaction.start(program, "Add block", true)) {
program.getMemory()
.createInitializedBlock(".text", ss.getAddress(0x00600000), 0x10000, (byte) 0,
monitor, false);
}
try (UndoableTransaction tid = tb.startTransaction()) {
DBTraceMemoryManager memory = tb.trace.getMemoryManager();
memory.addRegion("exe:.text", Range.atLeast(0L), tb.range(0x00400000, 0x0040ffff),
TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
TraceLocation from =
new DefaultTraceLocation(tb.trace, null, Range.atLeast(0L), tb.addr(0x00400000));
ProgramLocation to = new ProgramLocation(program, ss.getAddress(0x00600000));
DebuggerStaticMappingUtils.addMapping(from, to, 0x8000, false);
}
waitForProgram(program);
waitForDomainObject(tb.trace);
traceManager.activateTrace(tb.trace);
waitForSwing();
ProgramLocation loc;
goTo(tool, program, ss.getAddress(0x00601234));
@ -469,7 +481,7 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
}
@Test
public void testSyncToStaticListingDynamicToStaticOnSnapChange() throws Exception {
public void testSyncCursorToStaticListingDynamicToStaticOnSnapChange() throws Exception {
createAndOpenTrace();
createAndOpenProgramFromTrace();
intoProject(tb.trace);
@ -504,37 +516,15 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
traceManager.activateSnap(1);
waitForSwing();
ProgramLocation loc = codeViewer.getCurrentLocation();
ProgramLocation loc = codePlugin.getCurrentLocation();
assertEquals(program, loc.getProgram());
assertEquals(ss.getAddress(0x00601234), loc.getAddress());
}
@Test
public void testSyncToStaticListingDynamicToStaticOnLocationChange() throws Exception {
createAndOpenTrace();
createAndOpenProgramFromTrace();
intoProject(tb.trace);
intoProject(program);
public void testSyncCursorToStaticListingDynamicToStaticOnLocationChange() throws Exception {
createMappedTraceAndProgram();
AddressSpace ss = program.getAddressFactory().getDefaultAddressSpace();
try (UndoableTransaction tid = UndoableTransaction.start(program, "Add block", true)) {
program.getMemory()
.createInitializedBlock(".text", ss.getAddress(0x00600000), 0x10000, (byte) 0,
monitor, false);
}
try (UndoableTransaction tid = tb.startTransaction()) {
DBTraceMemoryManager memory = tb.trace.getMemoryManager();
memory.addRegion("exe:.text", Range.atLeast(0L), tb.range(0x00400000, 0x0040ffff),
TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
TraceLocation from =
new DefaultTraceLocation(tb.trace, null, Range.atLeast(0L), tb.addr(0x00400000));
ProgramLocation to = new ProgramLocation(program, ss.getAddress(0x00600000));
DebuggerStaticMappingUtils.addMapping(from, to, 0x8000, false);
}
waitForProgram(program);
waitForDomainObject(tb.trace);
traceManager.activateTrace(tb.trace);
waitForSwing();
listingProvider.getListingPanel()
.setCursorPosition(
@ -542,11 +532,41 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
EventTrigger.GUI_ACTION);
waitForSwing();
ProgramLocation loc = codeViewer.getCurrentLocation();
ProgramLocation loc = codePlugin.getCurrentLocation();
assertEquals(program, loc.getProgram());
assertEquals(ss.getAddress(0x00601234), loc.getAddress());
}
@Test
public void testSyncSelectionToStaticListingDynamicToStaticOnSelectionChange()
throws Exception {
createMappedTraceAndProgram();
AddressSpace ss = program.getAddressFactory().getDefaultAddressSpace();
listingProvider.getListingPanel()
.setSelection(new ProgramSelection(tb.addr(0x00401234), tb.addr(0x00404321)),
EventTrigger.GUI_ACTION);
waitForSwing();
assertEquals(tb.set(tb.range(ss, 0x00601234, 0x00604321)),
codePlugin.getCurrentSelection());
}
@Test
public void testSyncSelectionToStaticListingStaticToDynamicOnSelectionChange()
throws Exception {
createMappedTraceAndProgram();
AddressSpace ss = program.getAddressFactory().getDefaultAddressSpace();
codePlugin.getListingPanel()
.setSelection(
new ProgramSelection(tb.addr(ss, 0x00601234), tb.addr(ss, 0x00604321)),
EventTrigger.GUI_ACTION);
waitForSwing();
assertEquals(tb.set(tb.range(0x00401234, 0x00404321)), listingPlugin.getCurrentSelection());
}
@Test
public void testDynamicListingMarksTrackedRegister() throws Exception {
createAndOpenTrace();
@ -574,7 +594,7 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
}
@Test
public void testSyncToStaticListingMarksMappedTrackedRegister() throws Exception {
public void testSyncCursorToStaticListingMarksMappedTrackedRegister() throws Exception {
createAndOpenTrace();
createAndOpenProgramFromTrace();
intoProject(tb.trace);
@ -608,18 +628,18 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
waitForSwing();
assertListingBackgroundAt(DebuggerResources.DEFAULT_COLOR_REGISTER_MARKERS,
codeViewer.getListingPanel(), ss.getAddress(0x00601234), 0);
codePlugin.getListingPanel(), ss.getAddress(0x00601234), 0);
// For verifying static view didn't move
Address cur = codeViewer.getCurrentLocation().getAddress();
Address cur = codePlugin.getCurrentLocation().getAddress();
// Verify mark disappears when register value moves outside the mapped address range
traceManager.activateSnap(1);
waitForSwing();
// While we're here, ensure static view didn't track anywhere
assertEquals(cur, codeViewer.getCurrentLocation().getAddress());
assertListingBackgroundAt(Color.WHITE, codeViewer.getListingPanel(),
assertEquals(cur, codePlugin.getCurrentLocation().getAddress());
assertListingBackgroundAt(Color.WHITE, codePlugin.getListingPanel(),
ss.getAddress(0x00601234), 0);
}
@ -879,56 +899,100 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
@Ignore("Haven't specified this action, yet")
public void testActionTrackOtherRegister() {
// TODO: Actually, can we make this an arbitrary (pcode/sleigh?) expression.
TODO();
Unfinished.TODO();
}
@Test
public void testActionSyncToStaticListing() throws Exception {
assertTrue(listingProvider.actionSyncToStaticListing.isEnabled());
createAndOpenTrace();
createAndOpenProgramFromTrace();
intoProject(tb.trace);
intoProject(program);
public void testActionSyncCursorToStaticListing() throws Exception {
assertTrue(listingProvider.actionAutoSyncCursorWithStaticListing.isEnabled());
createMappedTraceAndProgram();
AddressSpace ss = program.getAddressFactory().getDefaultAddressSpace();
try (UndoableTransaction tid = UndoableTransaction.start(program, "Add block", true)) {
program.getMemory()
.createInitializedBlock(".text", ss.getAddress(0x00600000), 0x10000, (byte) 0,
monitor, false);
}
try (UndoableTransaction tid = tb.startTransaction()) {
DBTraceMemoryManager memory = tb.trace.getMemoryManager();
memory.addRegion("exe:.text", Range.atLeast(0L), tb.range(0x00400000, 0x0040ffff),
TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
TraceLocation from =
new DefaultTraceLocation(tb.trace, null, Range.atLeast(0L), tb.addr(0x00400000));
ProgramLocation to = new ProgramLocation(program, ss.getAddress(0x00600000));
DebuggerStaticMappingUtils.addMapping(from, to, 0x8000, false);
}
waitForProgram(program);
waitForDomainObject(tb.trace);
traceManager.activateTrace(tb.trace);
waitForSwing();
// Check default is on
assertTrue(listingProvider.actionSyncToStaticListing.isSelected());
assertTrue(listingProvider.actionAutoSyncCursorWithStaticListing.isSelected());
goTo(tool, program, ss.getAddress(0x00601234));
waitForSwing();
assertEquals(tb.addr(0x00401234), listingProvider.getLocation().getAddress());
performAction(listingProvider.actionSyncToStaticListing);
assertFalse(listingProvider.actionSyncToStaticListing.isSelected());
performAction(listingProvider.actionAutoSyncCursorWithStaticListing);
assertFalse(listingProvider.actionAutoSyncCursorWithStaticListing.isSelected());
goTo(tool, program, ss.getAddress(0x00608765));
waitForSwing();
// Verify the goTo was effective, but no change to dynamic listing location
assertEquals(ss.getAddress(0x00608765), codeViewer.getCurrentLocation().getAddress());
assertEquals(ss.getAddress(0x00608765), codePlugin.getCurrentLocation().getAddress());
assertEquals(tb.addr(0x00401234), listingProvider.getLocation().getAddress());
listingProvider.setSyncToStaticListing(true);
listingProvider.setAutoSyncCursorWithStaticListing(true);
// NOTE: Toggling adjusts the static listing, not the dynamic
waitForSwing();
assertTrue(listingProvider.actionSyncToStaticListing.isSelected());
assertEquals(ss.getAddress(0x00601234), codeViewer.getCurrentLocation().getAddress());
assertTrue(listingProvider.actionAutoSyncCursorWithStaticListing.isSelected());
assertEquals(ss.getAddress(0x00601234), codePlugin.getCurrentLocation().getAddress());
}
@Test
public void testActionSyncSelectionToStaticListing() throws Exception {
assertTrue(listingProvider.actionAutoSyncCursorWithStaticListing.isEnabled());
createMappedTraceAndProgram();
AddressSpace ss = program.getAddressFactory().getDefaultAddressSpace();
// Check default is on
assertTrue(listingProvider.actionAutoSyncSelectionWithStaticListing.isSelected());
makeSelection(tool, program, tb.range(ss, 0x00601234, 0x00604321));
goTo(tool, program, ss.getAddress(0x00601234));
waitForSwing();
assertEquals(tb.set(tb.range(0x00401234, 0x00404321)), listingPlugin.getCurrentSelection());
performAction(listingProvider.actionAutoSyncSelectionWithStaticListing);
assertFalse(listingProvider.actionAutoSyncSelectionWithStaticListing.isSelected());
goTo(tool, program, ss.getAddress(0x00608765));
makeSelection(tool, program, tb.range(ss, 0x00605678, 0x00608765));
waitForSwing();
// Verify the makeSelection was effective, but no change to dynamic listing location
assertEquals(tb.set(tb.range(ss, 0x00605678, 0x00608765)),
codePlugin.getCurrentSelection());
assertEquals(tb.set(tb.range(0x00401234, 0x00404321)), listingPlugin.getCurrentSelection());
listingProvider.setAutoSyncSelectionWithStaticListing(true);
// NOTE: Toggling adjusts the static listing, not the dynamic
waitForSwing();
assertTrue(listingProvider.actionAutoSyncSelectionWithStaticListing.isSelected());
assertEquals(tb.set(tb.range(ss, 0x00601234, 0x00604321)),
codePlugin.getCurrentSelection());
assertEquals(tb.set(tb.range(0x00401234, 0x00404321)), listingPlugin.getCurrentSelection());
}
@Test
public void testActionMapAddressesToStatic() throws Exception {
listingProvider.setAutoSyncSelectionWithStaticListing(false);
createMappedTraceAndProgram();
AddressSpace ss = program.getAddressFactory().getDefaultAddressSpace();
listingProvider.getListingPanel()
.setSelection(new ProgramSelection(tb.set(tb.range(0x00401234, 0x00404321))),
EventTrigger.GUI_ACTION);
assertTrue(codePlugin.getCurrentSelection().isEmpty());
performAction(listingProvider.actionSyncSelectionIntoStaticListing,
listingProvider.getActionContext(null), true);
assertEquals(tb.set(tb.range(ss, 0x00601234, 0x00604321)),
codePlugin.getCurrentSelection());
}
@Test
public void testActionMapAddressesToDynamic() throws Exception {
listingProvider.setAutoSyncSelectionWithStaticListing(false);
createMappedTraceAndProgram();
AddressSpace ss = program.getAddressFactory().getDefaultAddressSpace();
makeSelection(tool, program, tb.set(tb.range(ss, 0x00601234, 0x00604321)));
assertTrue(listingPlugin.getCurrentSelection().isEmpty());
performAction(listingProvider.actionSyncSelectionFromStaticListing,
codeProvider.getActionContext(null), true);
assertEquals(tb.set(tb.range(0x00401234, 0x00404321)),
listingPlugin.getCurrentSelection());
}
@Test
@ -962,8 +1026,8 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
// Verify it has immediately tracked on creation
assertEquals(tb.trace.getProgramView(), extraProvider.getLocation().getProgram());
assertEquals(thread1, extraProvider.current.getThread());
assertNull(getLocalAction(listingProvider, AbstractFollowsCurrentThreadAction.NAME));
assertNotNull(getLocalAction(extraProvider, AbstractFollowsCurrentThreadAction.NAME));
assertNull(getLocalAction(listingProvider, FollowsCurrentThreadAction.NAME));
assertNotNull(getLocalAction(extraProvider, FollowsCurrentThreadAction.NAME));
performAction(extraProvider.actionFollowsCurrentThread);
traceManager.activateThread(thread2);
@ -1111,18 +1175,18 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
TraceModule bin = tb.trace.getModuleManager()
.addLoadedModule("/bin/bash", "/bin/bash", tb.range(0x00400000, 0x0041ffff), 0);
bin.addSection("bash[.text]", tb.range(0x00400000, 0x0040ffff));
traceManager.activateTrace(tb.trace);
}
waitForDomainObject(tb.trace);
traceManager.activateTrace(tb.trace);
waitForSwing();
// In the module, but not in its section
listingPlugin.goTo(tb.addr(0x00411234), true);
assertTrue(listingPlugin.goTo(tb.addr(0x00411234), true));
waitForSwing();
waitForPass(() -> assertEquals(0,
consolePlugin.getRowCount(DebuggerMissingModuleActionContext.class)));
listingPlugin.goTo(tb.addr(0x00401234), true);
assertTrue(listingPlugin.goTo(tb.addr(0x00401234), true));
waitForSwing();
waitForPass(() -> assertEquals(1,
consolePlugin.getRowCount(DebuggerMissingModuleActionContext.class)));
@ -1141,13 +1205,13 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
tb.trace.getModuleManager()
.addLoadedModule("/bin/bash", "/bin/bash", tb.range(0x00400000, 0x0041ffff), 0);
traceManager.activateTrace(tb.trace);
}
waitForDomainObject(tb.trace);
traceManager.activateTrace(tb.trace);
waitForSwing();
// In the module, but not in its section
listingPlugin.goTo(tb.addr(0x00411234), true);
assertTrue(listingPlugin.goTo(tb.addr(0x00411234), true));
waitForSwing();
waitForPass(() -> assertEquals(1,
consolePlugin.getRowCount(DebuggerMissingModuleActionContext.class)));
@ -1413,7 +1477,7 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
}
@Test
public void testSyncToStaticListingOpensModule() throws Exception {
public void testSyncCursorToStaticListingOpensModule() throws Exception {
DebuggerConsolePlugin consolePlugin = addPlugin(tool, DebuggerConsolePlugin.class);
createAndOpenTrace();
@ -1459,7 +1523,7 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
}
@Test
public void testSyncToStaticLogsRecoverableProgram() throws Exception {
public void testSyncCursorToStaticLogsRecoverableProgram() throws Exception {
DebuggerConsolePlugin consolePlugin = addPlugin(tool, DebuggerConsolePlugin.class);
TestDummyDomainFolder root = new TestDummyDomainFolder(null, "root");
@ -1480,7 +1544,7 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
}
@Test
public void testSyncToStaticLogsUpgradeableProgram() throws Exception {
public void testSyncCursorToStaticLogsUpgradeableProgram() throws Exception {
DebuggerConsolePlugin consolePlugin = addPlugin(tool, DebuggerConsolePlugin.class);
TestDummyDomainFolder root = new TestDummyDomainFolder(null, "root");

View file

@ -47,7 +47,7 @@ import ghidra.app.plugin.core.clipboard.ClipboardPlugin;
import ghidra.app.plugin.core.debug.DebuggerCoordinates;
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest;
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
import ghidra.app.plugin.core.debug.gui.DebuggerResources.AbstractFollowsCurrentThreadAction;
import ghidra.app.plugin.core.debug.gui.DebuggerResources.FollowsCurrentThreadAction;
import ghidra.app.plugin.core.debug.gui.action.DebuggerGoToDialog;
import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingPlugin;
import ghidra.app.plugin.core.debug.service.editing.DebuggerStateEditingServicePlugin;
@ -725,8 +725,8 @@ public class DebuggerMemoryBytesProviderTest extends AbstractGhidraHeadedDebugge
// Verify it has immediately tracked on creation
assertEquals(tb.trace.getProgramView(), extraProvider.getProgram());
assertEquals(thread1, extraProvider.current.getThread());
assertNull(getLocalAction(memBytesProvider, AbstractFollowsCurrentThreadAction.NAME));
assertNotNull(getLocalAction(extraProvider, AbstractFollowsCurrentThreadAction.NAME));
assertNull(getLocalAction(memBytesProvider, FollowsCurrentThreadAction.NAME));
assertNotNull(getLocalAction(extraProvider, FollowsCurrentThreadAction.NAME));
performAction(extraProvider.actionFollowsCurrentThread);
traceManager.activateThread(thread2);