diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerListingPlugin/images/DebuggerListingPlugin.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerListingPlugin/images/DebuggerListingPlugin.png index b353abef39..00106d0cea 100644 Binary files a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerListingPlugin/images/DebuggerListingPlugin.png and b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerListingPlugin/images/DebuggerListingPlugin.png differ diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerResources.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerResources.java index eb6345bd37..7b46745fe9 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerResources.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerResources.java @@ -144,8 +144,7 @@ public interface DebuggerResources { ImageIcon ICON_AUTOREAD = ResourceManager.loadImage("images/autoread.png"); // TODO: Draw a real icon. - ImageIcon ICON_READ_MEMORY = ICON_REGIONS; - //ResourceManager.loadImage("images/read-memory.png"); + ImageIcon ICON_REFRESH_MEMORY = ICON_REFRESH; ImageIcon ICON_RENAME_SNAPSHOT = ICON_TIME; @@ -784,12 +783,12 @@ public interface DebuggerResources { } } - abstract class AbstractReadSelectedMemoryAction extends DockingAction { + abstract class AbstractRefreshSelectedMemoryAction extends DockingAction { public static final String NAME = "Read Selected Memory"; - public static final Icon ICON = ICON_READ_MEMORY; + public static final Icon ICON = ICON_REFRESH_MEMORY; public static final String HELP_ANCHOR = "read_memory"; - public AbstractReadSelectedMemoryAction(Plugin owner) { + public AbstractRefreshSelectedMemoryAction(Plugin owner) { super(NAME, owner.getName()); setDescription( "(Re-)read and record memory for the selected addresses into the trace database"); diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/DebuggerReadsMemoryTrait.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/DebuggerReadsMemoryTrait.java index 61585af598..94a4c3a53d 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/DebuggerReadsMemoryTrait.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/DebuggerReadsMemoryTrait.java @@ -16,7 +16,9 @@ package ghidra.app.plugin.core.debug.gui.action; import java.lang.invoke.MethodHandles; +import java.util.Collection; import java.util.Objects; +import java.util.concurrent.CompletableFuture; import docking.ActionContext; import docking.ComponentProvider; @@ -27,12 +29,16 @@ import docking.menu.MultiStateDockingAction; import docking.widgets.EventTrigger; import ghidra.app.plugin.core.debug.DebuggerCoordinates; import ghidra.app.plugin.core.debug.gui.DebuggerResources; -import ghidra.app.plugin.core.debug.gui.DebuggerResources.AbstractReadSelectedMemoryAction; +import ghidra.app.plugin.core.debug.gui.DebuggerResources.AbstractRefreshSelectedMemoryAction; import ghidra.app.plugin.core.debug.gui.action.AutoReadMemorySpec.AutoReadMemorySpecConfigFieldCodec; import ghidra.app.plugin.core.debug.utils.BackgroundUtils; import ghidra.app.services.TraceRecorder; import ghidra.app.services.TraceRecorderListener; import ghidra.app.util.viewer.listingpanel.AddressSetDisplayListener; +import ghidra.dbg.DebuggerObjectModel; +import ghidra.dbg.target.TargetMemory; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.util.PathMatcher; import ghidra.framework.options.SaveState; import ghidra.framework.plugintool.*; import ghidra.framework.plugintool.annotation.AutoConfigStateField; @@ -49,10 +55,10 @@ public abstract class DebuggerReadsMemoryTrait { protected static final AutoConfigState.ClassHandler CONFIG_STATE_HANDLER = AutoConfigState.wireHandler(DebuggerReadsMemoryTrait.class, MethodHandles.lookup()); - protected class ReadSelectedMemoryAction extends AbstractReadSelectedMemoryAction { + protected class RefreshSelectedMemoryAction extends AbstractRefreshSelectedMemoryAction { public static final String GROUP = DebuggerResources.GROUP_GENERAL; - public ReadSelectedMemoryAction() { + public RefreshSelectedMemoryAction() { super(plugin); setToolBarData(new ToolBarData(ICON, GROUP)); setEnabled(false); @@ -60,22 +66,40 @@ public abstract class DebuggerReadsMemoryTrait { @Override public void actionPerformed(ActionContext context) { - AddressSetView selection = getSelection(); - if (selection == null || selection.isEmpty() || !current.isAliveAndReadsPresent()) { + if (!current.isAliveAndReadsPresent()) { return; } + AddressSetView selection = getSelection(); + if (selection == null || selection.isEmpty()) { + selection = visible; + } + final AddressSetView sel = selection; Trace trace = current.getTrace(); TraceRecorder recorder = current.getRecorder(); - BackgroundUtils.async(tool, trace, NAME, true, true, false, - (__, monitor) -> recorder.captureProcessMemory(selection, monitor, false)); + BackgroundUtils.async(tool, trace, NAME, true, true, false, (_t, monitor) -> { + TargetObject target = recorder.getTarget(); + DebuggerObjectModel model = target.getModel(); + model.invalidateAllLocalCaches(); + PathMatcher memMatcher = target.getSchema().searchFor(TargetMemory.class, true); + Collection memories = memMatcher.getCachedSuccessors(target).values(); + CompletableFuture[] requests = memories.stream() + .map(TargetObject::invalidateCaches) + .toArray(CompletableFuture[]::new); + return CompletableFuture.allOf(requests).thenCompose(_r -> { + return recorder.captureProcessMemory(sel, monitor, false); + }); + }); } @Override public boolean isEnabledForContext(ActionContext context) { - AddressSetView selection = getSelection(); - if (selection == null || selection.isEmpty() || !current.isAliveAndReadsPresent()) { + if (!current.isAliveAndReadsPresent()) { return false; } + AddressSetView selection = getSelection(); + if (selection == null || selection.isEmpty()) { + selection = visible; + } TraceRecorder recorder = current.getRecorder(); // TODO: Either allow partial, or provide action to intersect with accessible if (!recorder.getAccessibleProcessMemory().contains(selection)) { @@ -96,7 +120,7 @@ public abstract class DebuggerReadsMemoryTrait { } private void snapshotAdded(TraceSnapshot snapshot) { - actionReadSelected.updateEnabled(null); + actionRefreshSelected.updateEnabled(null); } private void memStateChanged(TraceAddressSnapRange range, TraceMemoryState oldIsNull, @@ -120,7 +144,7 @@ public abstract class DebuggerReadsMemoryTrait { @Override public void processMemoryAccessibilityChanged(TraceRecorder recorder) { Swing.runIfSwingOrRunLater(() -> { - actionReadSelected.updateEnabled(null); + actionRefreshSelected.updateEnabled(null); }); } } @@ -137,7 +161,7 @@ public abstract class DebuggerReadsMemoryTrait { } protected MultiStateDockingAction actionAutoRead; - protected ReadSelectedMemoryAction actionReadSelected; + protected RefreshSelectedMemoryAction actionRefreshSelected; private final AutoReadMemorySpec defaultAutoSpec = AutoReadMemorySpec.fromConfigName(VisibleROOnceAutoReadMemorySpec.CONFIG_NAME); @@ -257,10 +281,10 @@ public abstract class DebuggerReadsMemoryTrait { } } - public DockingAction installReadSelectedAction() { - actionReadSelected = new ReadSelectedMemoryAction(); - provider.addLocalAction(actionReadSelected); - return actionReadSelected; + public DockingAction installRefreshSelectedAction() { + actionRefreshSelected = new RefreshSelectedMemoryAction(); + provider.addLocalAction(actionRefreshSelected); + return actionRefreshSelected; } public AddressSetDisplayListener getDisplayListener() { diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingProvider.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingProvider.java index e9f70ac31b..c2270b6ac9 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingProvider.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingProvider.java @@ -244,7 +244,7 @@ public class DebuggerListingProvider extends CodeViewerProvider { protected SyncToStaticListingAction actionSyncToStaticListing; protected FollowsCurrentThreadAction actionFollowsCurrentThread; protected MultiStateDockingAction actionAutoReadMemory; - protected DockingAction actionReadSelectedMemory; + protected DockingAction actionRefreshSelectedMemory; protected DockingAction actionOpenProgram; protected MultiStateDockingAction actionTrackLocation; @@ -643,7 +643,7 @@ public class DebuggerListingProvider extends CodeViewerProvider { actionGoTo = goToTrait.installAction(); actionTrackLocation = trackingTrait.installAction(); actionAutoReadMemory = readsMemTrait.installAutoReadAction(); - actionReadSelectedMemory = readsMemTrait.installReadSelectedAction(); + actionRefreshSelectedMemory = readsMemTrait.installRefreshSelectedAction(); actionOpenProgram = OpenProgramAction.builder(plugin) .withContext(DebuggerOpenProgramActionContext.class) diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesProvider.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesProvider.java index ae81e2ed01..421d0c41a7 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesProvider.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesProvider.java @@ -149,7 +149,7 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi protected DockingAction actionGoTo; protected FollowsCurrentThreadAction actionFollowsCurrentThread; protected MultiStateDockingAction actionAutoReadMemory; - protected DockingAction actionReadSelectedMemory; + protected DockingAction actionRefreshSelectedMemory; protected MultiStateDockingAction actionTrackLocation; protected ForMemoryBytesGoToTrait goToTrait; @@ -263,7 +263,7 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi actionGoTo = goToTrait.installAction(); actionTrackLocation = trackingTrait.installAction(); actionAutoReadMemory = readsMemTrait.installAutoReadAction(); - actionReadSelectedMemory = readsMemTrait.installReadSelectedAction(); + actionRefreshSelectedMemory = readsMemTrait.installRefreshSelectedAction(); } @Override diff --git a/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingPluginScreenShots.java b/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingPluginScreenShots.java index 7a38cc5eb7..60a689b625 100644 --- a/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingPluginScreenShots.java +++ b/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingPluginScreenShots.java @@ -80,7 +80,7 @@ public class DebuggerListingPluginScreenShots extends GhidraScreenShotGenerator "clone", global, SourceType.USER_DEFINED); TraceSymbol childLabel = symbolManager .labels() - .create(snap, null, tb.addr(0x00400034), + .create(snap, null, tb.addr(0x00400032), "child", global, SourceType.USER_DEFINED); @SuppressWarnings("unused") TraceSymbol exitLabel = symbolManager diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingProviderTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingProviderTest.java index ed9bc6f32f..974712df6a 100644 --- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingProviderTest.java +++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingProviderTest.java @@ -933,7 +933,7 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI byte[] data = incBlock(); byte[] zero = new byte[data.length]; ByteBuffer buf = ByteBuffer.allocate(data.length); - assertFalse(listingProvider.actionReadSelectedMemory.isEnabled()); + assertFalse(listingProvider.actionRefreshSelectedMemory.isEnabled()); listingProvider.setAutoReadMemorySpec(readNone); // To verify enabled requires live target @@ -948,12 +948,12 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI traceManager.activateTrace(tb.trace); waitForSwing(); // Still - assertFalse(listingProvider.actionReadSelectedMemory.isEnabled()); + assertFalse(listingProvider.actionRefreshSelectedMemory.isEnabled()); listingProvider.setSelection(sel); waitForSwing(); // Still - assertFalse(listingProvider.actionReadSelectedMemory.isEnabled()); + assertFalse(listingProvider.actionRefreshSelectedMemory.isEnabled()); // Now, simulate the sequence that typically enables the action createTestModel(); @@ -970,12 +970,12 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI // NOTE: recordTargetContainerAndOpenTrace has already activated the trace // Action is still disabled, because it requires a selection - assertFalse(listingProvider.actionReadSelectedMemory.isEnabled()); + assertFalse(listingProvider.actionRefreshSelectedMemory.isEnabled()); listingProvider.setSelection(sel); waitForSwing(); // Now, it should be enabled - assertTrue(listingProvider.actionReadSelectedMemory.isEnabled()); + assertTrue(listingProvider.actionRefreshSelectedMemory.isEnabled()); // First check nothing captured yet buf.clear(); @@ -984,7 +984,7 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI assertArrayEquals(zero, buf.array()); // Verify that the action performs the expected task - performAction(listingProvider.actionReadSelectedMemory); + performAction(listingProvider.actionRefreshSelectedMemory); waitForBusyTool(tool); waitForDomainObject(trace); @@ -999,28 +999,28 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI // Verify that setting the memory inaccessible disables the action mb.testProcess1.memory.setAccessible(false); - waitForPass(() -> assertFalse(listingProvider.actionReadSelectedMemory.isEnabled())); + waitForPass(() -> assertFalse(listingProvider.actionRefreshSelectedMemory.isEnabled())); // Verify that setting it accessible re-enables it (assuming we still have selection) mb.testProcess1.memory.setAccessible(true); - waitForPass(() -> assertTrue(listingProvider.actionReadSelectedMemory.isEnabled())); + waitForPass(() -> assertTrue(listingProvider.actionRefreshSelectedMemory.isEnabled())); // Verify that moving into the past disables the action TraceSnapshot forced = recorder.forceSnapshot(); waitForSwing(); // UI Wants to sync with new snap. Wait.... traceManager.activateSnap(forced.getKey() - 1); waitForSwing(); - assertFalse(listingProvider.actionReadSelectedMemory.isEnabled()); + assertFalse(listingProvider.actionRefreshSelectedMemory.isEnabled()); // Verify that advancing to the present enables the action (assuming a selection) traceManager.activateSnap(forced.getKey()); waitForSwing(); - assertTrue(listingProvider.actionReadSelectedMemory.isEnabled()); + assertTrue(listingProvider.actionRefreshSelectedMemory.isEnabled()); // Verify that stopping the recording disables the action recorder.stopRecording(); waitForSwing(); - assertFalse(listingProvider.actionReadSelectedMemory.isEnabled()); + assertFalse(listingProvider.actionRefreshSelectedMemory.isEnabled()); // TODO: When resume recording is implemented, verify action is enabled with selection } diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesProviderTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesProviderTest.java index d6127cf6c2..41d849faad 100644 --- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesProviderTest.java +++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesProviderTest.java @@ -735,7 +735,7 @@ public class DebuggerMemoryBytesProviderTest extends AbstractGhidraHeadedDebugge byte[] data = incBlock(); byte[] zero = new byte[data.length]; ByteBuffer buf = ByteBuffer.allocate(data.length); - assertFalse(memBytesProvider.actionReadSelectedMemory.isEnabled()); + assertFalse(memBytesProvider.actionRefreshSelectedMemory.isEnabled()); runSwing(() -> memBytesProvider.setAutoReadMemorySpec(readNone)); // To verify enabled requires live target @@ -750,12 +750,12 @@ public class DebuggerMemoryBytesProviderTest extends AbstractGhidraHeadedDebugge traceManager.activateTrace(tb.trace); waitForSwing(); // Still - assertFalse(memBytesProvider.actionReadSelectedMemory.isEnabled()); + assertFalse(memBytesProvider.actionRefreshSelectedMemory.isEnabled()); memBytesProvider.setSelection(sel); waitForSwing(); // Still - assertFalse(memBytesProvider.actionReadSelectedMemory.isEnabled()); + assertFalse(memBytesProvider.actionRefreshSelectedMemory.isEnabled()); // Now, simulate the sequence that typically enables the action createTestModel(); @@ -772,12 +772,12 @@ public class DebuggerMemoryBytesProviderTest extends AbstractGhidraHeadedDebugge // NOTE: recordTargetContainerAndOpenTrace has already activated the trace // Action is still disabled, because it requires a selection - assertFalse(memBytesProvider.actionReadSelectedMemory.isEnabled()); + assertFalse(memBytesProvider.actionRefreshSelectedMemory.isEnabled()); memBytesProvider.setSelection(sel); waitForSwing(); // Now, it should be enabled - assertTrue(memBytesProvider.actionReadSelectedMemory.isEnabled()); + assertTrue(memBytesProvider.actionRefreshSelectedMemory.isEnabled()); // First check nothing recorded yet buf.clear(); @@ -786,7 +786,7 @@ public class DebuggerMemoryBytesProviderTest extends AbstractGhidraHeadedDebugge assertArrayEquals(zero, buf.array()); // Verify that the action performs the expected task - performAction(memBytesProvider.actionReadSelectedMemory); + performAction(memBytesProvider.actionRefreshSelectedMemory); waitForBusyTool(tool); waitForDomainObject(trace); @@ -801,28 +801,28 @@ public class DebuggerMemoryBytesProviderTest extends AbstractGhidraHeadedDebugge // Verify that setting the memory inaccessible disables the action mb.testProcess1.memory.setAccessible(false); - waitForPass(() -> assertFalse(memBytesProvider.actionReadSelectedMemory.isEnabled())); + waitForPass(() -> assertFalse(memBytesProvider.actionRefreshSelectedMemory.isEnabled())); // Verify that setting it accessible re-enables it (assuming we still have selection) mb.testProcess1.memory.setAccessible(true); - waitForPass(() -> assertTrue(memBytesProvider.actionReadSelectedMemory.isEnabled())); + waitForPass(() -> assertTrue(memBytesProvider.actionRefreshSelectedMemory.isEnabled())); // Verify that moving into the past disables the action TraceSnapshot forced = recorder.forceSnapshot(); waitForSwing(); // UI Wants to sync with new snap. Wait.... traceManager.activateSnap(forced.getKey() - 1); waitForSwing(); - assertFalse(memBytesProvider.actionReadSelectedMemory.isEnabled()); + assertFalse(memBytesProvider.actionRefreshSelectedMemory.isEnabled()); // Verify that advancing to the present enables the action (assuming a selection) traceManager.activateSnap(forced.getKey()); waitForSwing(); - assertTrue(memBytesProvider.actionReadSelectedMemory.isEnabled()); + assertTrue(memBytesProvider.actionRefreshSelectedMemory.isEnabled()); // Verify that stopping the recording disables the action recorder.stopRecording(); waitForSwing(); - assertFalse(memBytesProvider.actionReadSelectedMemory.isEnabled()); + assertFalse(memBytesProvider.actionRefreshSelectedMemory.isEnabled()); // TODO: When resume recording is implemented, verify action is enabled with selection }