mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
GP-1451: Add sync selection actions, toggles
This commit is contained in:
parent
ccbf264116
commit
cfdf1051a1
32 changed files with 1068 additions and 424 deletions
|
@ -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>
|
||||
|
||||
|
|
|
@ -325,9 +325,9 @@
|
|||
|
||||
<H3><A name="toggle_update_while_running">Update While Running</A></H3>
|
||||
|
||||
<P>By default, events are passed to the Objects Viewer even while the target is running.
|
||||
The resulting changes in the GUI may be distracting for some. To disable updates to the
|
||||
Objects Viewer, toggle "Updates While Running" off.</P>
|
||||
<P>By default, events are passed to the Objects Viewer even while the target is running. The
|
||||
resulting changes in the GUI may be distracting for some. To disable updates to the Objects
|
||||
Viewer, toggle "Updates While Running" off.</P>
|
||||
|
||||
<H2><A name="color"></A>Color Options</H2>
|
||||
|
||||
|
|
|
@ -833,20 +833,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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -884,15 +932,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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 actionReadSelectedMemory;
|
||||
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();
|
||||
|
|
|
@ -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 actionReadSelectedMemory;
|
||||
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();
|
||||
|
|
|
@ -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;
|
||||
|
@ -602,21 +601,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) {
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -927,7 +927,7 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
|
|||
synchronized (lock) {
|
||||
InfoPerProgram info = requireTrackedInfo(program);
|
||||
if (info == null) {
|
||||
return null;
|
||||
return Map.of();
|
||||
}
|
||||
return info.getOpenMappedViews(set);
|
||||
}
|
||||
|
|
|
@ -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.DebuggerGoToDialog;
|
||||
import ghidra.app.plugin.core.debug.gui.console.DebuggerConsolePlugin;
|
||||
import ghidra.app.plugin.core.debug.gui.console.DebuggerConsoleProvider.BoundAction;
|
||||
|
@ -47,6 +48,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;
|
||||
|
@ -74,15 +76,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) {
|
||||
|
@ -101,6 +107,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();
|
||||
|
@ -418,31 +451,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));
|
||||
|
@ -468,7 +480,7 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testSyncToStaticListingDynamicToStaticOnSnapChange() throws Exception {
|
||||
public void testSyncCursorToStaticListingDynamicToStaticOnSnapChange() throws Exception {
|
||||
createAndOpenTrace();
|
||||
createAndOpenProgramFromTrace();
|
||||
intoProject(tb.trace);
|
||||
|
@ -503,37 +515,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(
|
||||
|
@ -541,11 +531,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();
|
||||
|
@ -573,7 +593,7 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testSyncToStaticListingMarksMappedTrackedRegister() throws Exception {
|
||||
public void testSyncCursorToStaticListingMarksMappedTrackedRegister() throws Exception {
|
||||
createAndOpenTrace();
|
||||
createAndOpenProgramFromTrace();
|
||||
intoProject(tb.trace);
|
||||
|
@ -607,18 +627,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);
|
||||
}
|
||||
|
||||
|
@ -832,56 +852,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
|
||||
|
@ -915,8 +979,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);
|
||||
|
@ -1064,17 +1128,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)));
|
||||
|
@ -1093,12 +1158,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)));
|
||||
|
@ -1364,7 +1430,7 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testSyncToStaticListingOpensModule() throws Exception {
|
||||
public void testSyncCursorToStaticListingOpensModule() throws Exception {
|
||||
DebuggerConsolePlugin consolePlugin = addPlugin(tool, DebuggerConsolePlugin.class);
|
||||
|
||||
createAndOpenTrace();
|
||||
|
@ -1410,7 +1476,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");
|
||||
|
@ -1431,7 +1497,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");
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue