diff --git a/Ghidra/Debug/Debugger-api/src/main/java/ghidra/app/services/DebuggerStaticMappingService.java b/Ghidra/Debug/Debugger-api/src/main/java/ghidra/app/services/DebuggerStaticMappingService.java
index a51084c001..a02b83d021 100644
--- a/Ghidra/Debug/Debugger-api/src/main/java/ghidra/app/services/DebuggerStaticMappingService.java
+++ b/Ghidra/Debug/Debugger-api/src/main/java/ghidra/app/services/DebuggerStaticMappingService.java
@@ -23,7 +23,8 @@ import ghidra.debug.api.modules.ModuleMapProposal.ModuleMapEntry;
import ghidra.debug.api.modules.RegionMapProposal.RegionMapEntry;
import ghidra.debug.api.modules.SectionMapProposal.SectionMapEntry;
import ghidra.framework.model.DomainFile;
-import ghidra.program.model.address.*;
+import ghidra.program.model.address.AddressSetView;
+import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.util.ProgramLocation;
@@ -50,151 +51,6 @@ import ghidra.util.task.TaskMonitor;
*/
public interface DebuggerStaticMappingService {
- /**
- * A pair for describing sets of mapped addresses
- *
- *
- * Note, the natural order is by the destination address.
- */
- public class MappedAddressRange implements Comparable {
-
- private final AddressRange srcRange;
- private final AddressRange dstRange;
- private final int hashCode;
- private final long shift;
-
- public MappedAddressRange(AddressRange srcRange, AddressRange dstRange) {
- this.srcRange = srcRange;
- this.dstRange = dstRange;
- this.hashCode = Objects.hash(dstRange, srcRange);
- this.shift = dstRange.getMinAddress().getOffset() -
- srcRange.getMinAddress().getOffset();
- }
-
- @Override
- public String toString() {
- return "";
- }
-
- /**
- * Get the shift from the source address range to this address range
- *
- *
- * The meaning depends on what returned this view. If this view is the "static" range, then
- * this shift describes what was added to the offset of the "dynamic" address to get a
- * particular address in the "static" range.
- *
- * @return the shift
- */
- public long getShift() {
- return shift;
- }
-
- /**
- * Map an address in the source range to the corresponding address in the destination range
- *
- * @param saddr the source address (not validated)
- * @return the destination address
- */
- public Address mapSourceToDestination(Address saddr) {
- return dstRange.getAddressSpace().getAddress(saddr.getOffset() + shift);
- }
-
- /**
- * Map an address in the destination range to the corresponding address in the source range
- *
- * @param daddr the destination address (not validated)
- * @return the source address
- */
- public Address mapDestinationToSource(Address daddr) {
- return srcRange.getAddressSpace().getAddress(daddr.getOffset() - shift);
- }
-
- /**
- * Map a sub-range of the source to the corresponding sub-range of the destination
- *
- * @param srng the source sub-range
- * @return the destination sub-range
- */
- public AddressRange mapSourceToDestination(AddressRange srng) {
- try {
- return new AddressRangeImpl(mapSourceToDestination(srng.getMinAddress()),
- srng.getLength());
- }
- catch (AddressOverflowException e) {
- throw new IllegalArgumentException(e);
- }
- }
-
- /**
- * Map a sub-range of the destination to the corresponding sub-range of the source
- *
- * @param drng the destination sub-range
- * @return the source sub-range
- */
- public AddressRange mapDestinationToSource(AddressRange drng) {
- try {
- return new AddressRangeImpl(mapDestinationToSource(drng.getMinAddress()),
- drng.getLength());
- }
- catch (AddressOverflowException e) {
- throw new IllegalArgumentException(e);
- }
- }
-
- /**
- * Get the source address range
- *
- * @return the address range
- */
- public AddressRange getSourceAddressRange() {
- return srcRange;
- }
-
- /**
- * Get the destination address range
- *
- * @return the address range
- */
- public AddressRange getDestinationAddressRange() {
- return dstRange;
- }
-
- @Override
- public int compareTo(MappedAddressRange that) {
- int c;
- c = this.dstRange.compareTo(that.dstRange);
- if (c != 0) {
- return c;
- }
- c = this.srcRange.compareTo(that.srcRange);
- if (c != 0) {
- return c;
- }
- return 0;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (!(obj instanceof MappedAddressRange)) {
- return false;
- }
- MappedAddressRange that = (MappedAddressRange) obj;
- if (!this.dstRange.equals(that.dstRange)) {
- return false;
- }
- if (!this.srcRange.equals(that.srcRange)) {
- return false;
- }
- return true;
- }
-
- @Override
- public int hashCode() {
- return hashCode;
- }
- }
-
/**
* Add a static mapping (relocation) from the given trace to the given program
*
diff --git a/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/modules/MappedAddressRange.java b/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/modules/MappedAddressRange.java
new file mode 100644
index 0000000000..c57f732ee9
--- /dev/null
+++ b/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/modules/MappedAddressRange.java
@@ -0,0 +1,165 @@
+/* ###
+ * 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.debug.api.modules;
+
+import java.util.Objects;
+
+import ghidra.program.model.address.*;
+
+/**
+ * A pair for describing sets of mapped addresses
+ *
+ *
+ * Note, the natural order is by the destination address.
+ */
+public class MappedAddressRange implements Comparable {
+
+ private final AddressRange srcRange;
+ private final AddressRange dstRange;
+ private final int hashCode;
+ private final long shift;
+
+ public MappedAddressRange(AddressRange srcRange, AddressRange dstRange) {
+ this.srcRange = srcRange;
+ this.dstRange = dstRange;
+ this.hashCode = Objects.hash(dstRange, srcRange);
+ this.shift = dstRange.getMinAddress().getOffset() -
+ srcRange.getMinAddress().getOffset();
+ }
+
+ @Override
+ public String toString() {
+ return "";
+ }
+
+ /**
+ * Get the shift from the source address range to this address range
+ *
+ *
+ * The meaning depends on what returned this view. If this view is the "static" range, then
+ * this shift describes what was added to the offset of the "dynamic" address to get a
+ * particular address in the "static" range.
+ *
+ * @return the shift
+ */
+ public long getShift() {
+ return shift;
+ }
+
+ /**
+ * Map an address in the source range to the corresponding address in the destination range
+ *
+ * @param saddr the source address (not validated)
+ * @return the destination address
+ */
+ public Address mapSourceToDestination(Address saddr) {
+ return dstRange.getAddressSpace().getAddress(saddr.getOffset() + shift);
+ }
+
+ /**
+ * Map an address in the destination range to the corresponding address in the source range
+ *
+ * @param daddr the destination address (not validated)
+ * @return the source address
+ */
+ public Address mapDestinationToSource(Address daddr) {
+ return srcRange.getAddressSpace().getAddress(daddr.getOffset() - shift);
+ }
+
+ /**
+ * Map a sub-range of the source to the corresponding sub-range of the destination
+ *
+ * @param srng the source sub-range
+ * @return the destination sub-range
+ */
+ public AddressRange mapSourceToDestination(AddressRange srng) {
+ try {
+ return new AddressRangeImpl(mapSourceToDestination(srng.getMinAddress()),
+ srng.getLength());
+ }
+ catch (AddressOverflowException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /**
+ * Map a sub-range of the destination to the corresponding sub-range of the source
+ *
+ * @param drng the destination sub-range
+ * @return the source sub-range
+ */
+ public AddressRange mapDestinationToSource(AddressRange drng) {
+ try {
+ return new AddressRangeImpl(mapDestinationToSource(drng.getMinAddress()),
+ drng.getLength());
+ }
+ catch (AddressOverflowException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /**
+ * Get the source address range
+ *
+ * @return the address range
+ */
+ public AddressRange getSourceAddressRange() {
+ return srcRange;
+ }
+
+ /**
+ * Get the destination address range
+ *
+ * @return the address range
+ */
+ public AddressRange getDestinationAddressRange() {
+ return dstRange;
+ }
+
+ @Override
+ public int compareTo(MappedAddressRange that) {
+ int c;
+ c = this.dstRange.compareTo(that.dstRange);
+ if (c != 0) {
+ return c;
+ }
+ c = this.srcRange.compareTo(that.srcRange);
+ if (c != 0) {
+ return c;
+ }
+ return 0;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof MappedAddressRange)) {
+ return false;
+ }
+ MappedAddressRange that = (MappedAddressRange) obj;
+ if (!this.dstRange.equals(that.dstRange)) {
+ return false;
+ }
+ if (!this.srcRange.equals(that.srcRange)) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return hashCode;
+ }
+}
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/copying/DebuggerCopyIntoProgramDialog.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/copying/DebuggerCopyIntoProgramDialog.java
index 2787694d29..6c91edbecf 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/copying/DebuggerCopyIntoProgramDialog.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/copying/DebuggerCopyIntoProgramDialog.java
@@ -32,7 +32,7 @@ import docking.widgets.table.*;
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
import ghidra.app.plugin.core.debug.gui.copying.DebuggerCopyPlan.Copier;
import ghidra.app.services.*;
-import ghidra.app.services.DebuggerStaticMappingService.MappedAddressRange;
+import ghidra.debug.api.modules.MappedAddressRange;
import ghidra.debug.api.target.Target;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.framework.plugintool.PluginTool;
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/data/DefaultPcodeDebuggerMemoryAccess.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/data/DefaultPcodeDebuggerMemoryAccess.java
index 9ce860c2a4..e2d1ade782 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/data/DefaultPcodeDebuggerMemoryAccess.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/data/DefaultPcodeDebuggerMemoryAccess.java
@@ -20,8 +20,8 @@ import java.util.concurrent.CompletableFuture;
import ghidra.app.plugin.core.debug.utils.AbstractMappedMemoryBytesVisitor;
import ghidra.app.services.DebuggerStaticMappingService;
-import ghidra.app.services.DebuggerStaticMappingService.MappedAddressRange;
import ghidra.debug.api.emulation.PcodeDebuggerMemoryAccess;
+import ghidra.debug.api.modules.MappedAddressRange;
import ghidra.debug.api.target.Target;
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.pcode.exec.PcodeExecutorStatePiece;
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/DebuggerStaticMappingContext.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/DebuggerStaticMappingContext.java
new file mode 100644
index 0000000000..3507c43778
--- /dev/null
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/DebuggerStaticMappingContext.java
@@ -0,0 +1,343 @@
+/* ###
+ * 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.service.modules;
+
+import java.net.URL;
+import java.util.*;
+import java.util.concurrent.Executor;
+import java.util.stream.Collectors;
+
+import ghidra.app.plugin.core.debug.utils.ProgramLocationUtils;
+import ghidra.async.AsyncUtils;
+import ghidra.debug.api.modules.DebuggerStaticMappingChangeListener;
+import ghidra.debug.api.modules.MappedAddressRange;
+import ghidra.program.model.address.AddressSetView;
+import ghidra.program.model.listing.Program;
+import ghidra.program.util.ProgramLocation;
+import ghidra.trace.model.*;
+import ghidra.trace.model.program.TraceProgramView;
+import ghidra.util.Msg;
+import ghidra.util.datastruct.ListenerSet;
+
+public class DebuggerStaticMappingContext {
+
+ record ChangeCollector(DebuggerStaticMappingContext ctx, Set traces,
+ Set programs) implements AutoCloseable {
+
+ static Set subtract(Set a, Set b) {
+ Set result = new HashSet<>(a);
+ result.removeAll(b);
+ return result;
+ }
+
+ public ChangeCollector(DebuggerStaticMappingContext ctx) {
+ this(ctx, new HashSet<>(), new HashSet<>());
+ }
+
+ public void traceAffected(Trace trace) {
+ this.traces.add(trace);
+ }
+
+ public void programAffected(Program program) {
+ if (program != null) {
+ this.programs.add(program);
+ }
+ }
+
+ @Override
+ public void close() {
+ ctx.changeListeners.getProxy().mappingsChanged(traces, programs);
+ }
+ }
+
+ final Map traceInfoByTrace = new HashMap<>();
+ final Map programInfoByProgram = new HashMap<>();
+ final Map programInfoByUrl = new HashMap<>();
+
+ final Object lock = new Object();
+
+ final Executor executor;
+ private final ListenerSet changeListeners =
+ new ListenerSet<>(DebuggerStaticMappingChangeListener.class, true);
+
+ public DebuggerStaticMappingContext() {
+ this(AsyncUtils.DIRECT_EXECUTOR);
+ }
+
+ public DebuggerStaticMappingContext(Executor executor) {
+ this.executor = executor;
+ }
+
+ public void addChangeListener(DebuggerStaticMappingChangeListener l) {
+ changeListeners.add(l);
+ }
+
+ public void removeChangeListener(DebuggerStaticMappingChangeListener l) {
+ changeListeners.remove(l);
+ }
+
+ public ChangeCollector collectChanges() {
+ return new ChangeCollector(this);
+ }
+
+ public void addProgram(ChangeCollector cc, Program program) {
+ synchronized (lock) {
+ processAddedProgram(cc, program);
+ }
+ }
+
+ public void removeProgram(ChangeCollector cc, Program program) {
+ synchronized (lock) {
+ processRemovedProgramInfo(cc, requireTrackedInfo(program));
+ }
+ }
+
+ public void setPrograms(ChangeCollector cc, Set programs) {
+ synchronized (lock) {
+ Set removed = programInfoByProgram.values()
+ .stream()
+ .filter(i -> !programs.contains(i.program) || !i.urlMatches())
+ .collect(Collectors.toSet());
+ processRemovedProgramInfos(cc, removed);
+ Set added = ChangeCollector.subtract(programs, programInfoByProgram.keySet());
+ processAddedPrograms(cc, added);
+ }
+ }
+
+ public void addTrace(ChangeCollector cc, Trace trace) {
+ synchronized (lock) {
+ processAddedTrace(cc, trace);
+ }
+ }
+
+ public void removeTrace(ChangeCollector cc, Trace trace) {
+ synchronized (lock) {
+ processRemovedTrace(cc, trace);
+ }
+ }
+
+ public void setTraces(ChangeCollector cc, Set traces) {
+ synchronized (lock) {
+ Set oldTraces = traceInfoByTrace.keySet();
+
+ Set removed = ChangeCollector.subtract(oldTraces, traces);
+ Set added = ChangeCollector.subtract(traces, oldTraces);
+
+ processRemovedTraces(cc, removed);
+ processAddedTraces(cc, added);
+ }
+ }
+
+ protected T noTraceInfo() {
+ Msg.debug(this, "The given trace is not open in this tool " +
+ "(or the service hasn't received and processed the open-trace event, yet)");
+ return null;
+ }
+
+ protected T noProgramInfo() {
+ Msg.debug(this, "The given program is not open in this tool " +
+ "(or the service hasn't received and processed the open-program event, yet)");
+ return null;
+ }
+
+ protected T noProject() {
+ return DebuggerStaticMappingUtils.noProject(this);
+ }
+
+ void checkAndClearProgram(ChangeCollector cc, MappingEntry me) {
+ InfoPerProgram info = programInfoByUrl.get(me.getStaticProgramUrl());
+ if (info == null) {
+ return;
+ }
+ info.clearProgram(cc, me);
+ }
+
+ void checkAndFillProgram(ChangeCollector cc, MappingEntry me) {
+ InfoPerProgram info = programInfoByUrl.get(me.getStaticProgramUrl());
+ if (info == null) {
+ return;
+ }
+ info.fillProgram(cc, me);
+ }
+
+ void processRemovedProgramInfos(ChangeCollector cc, Set removed) {
+ for (InfoPerProgram info : removed) {
+ processRemovedProgramInfo(cc, info);
+ }
+ }
+
+ void processRemovedProgramInfo(ChangeCollector cc, InfoPerProgram info) {
+ programInfoByProgram.remove(info.program);
+ programInfoByUrl.remove(info.url);
+ info.clearEntries(cc);
+ }
+
+ void processAddedPrograms(ChangeCollector cc, Set added) {
+ for (Program program : added) {
+ processAddedProgram(cc, program);
+ }
+ }
+
+ void processAddedProgram(ChangeCollector cc, Program program) {
+ InfoPerProgram info = new InfoPerProgram(this, program);
+ programInfoByProgram.put(program, info);
+ programInfoByUrl.put(info.url, info);
+ info.fillEntries(cc);
+ }
+
+ void processRemovedTraces(ChangeCollector cc, Set removed) {
+ for (Trace trace : removed) {
+ processRemovedTrace(cc, trace);
+ }
+ }
+
+ void processRemovedTrace(ChangeCollector cc, Trace trace) {
+ InfoPerTrace info = traceInfoByTrace.remove(trace);
+ info.removeEntries(cc);
+ }
+
+ void processAddedTraces(ChangeCollector cc, Set added) {
+ for (Trace trace : added) {
+ processAddedTrace(cc, trace);
+ }
+ }
+
+ void processAddedTrace(ChangeCollector cc, Trace trace) {
+ InfoPerTrace info = new InfoPerTrace(this, trace);
+ traceInfoByTrace.put(trace, info);
+ info.resyncEntries(cc);
+ }
+
+ protected InfoPerTrace requireTrackedInfo(Trace trace) {
+ InfoPerTrace info = traceInfoByTrace.get(trace);
+ if (info == null) {
+ return noTraceInfo();
+ }
+ return info;
+ }
+
+ protected InfoPerProgram requireTrackedInfo(Program program) {
+ InfoPerProgram info = programInfoByProgram.get(program);
+ if (info == null) {
+ return noProgramInfo();
+ }
+ return info;
+ }
+
+ public Set getOpenMappedProgramsAtSnap(Trace trace, long snap) {
+ synchronized (lock) {
+ InfoPerTrace info = requireTrackedInfo(trace);
+ if (info == null) {
+ return null;
+ }
+ return info.getOpenMappedProgramsAtSnap(snap);
+ }
+ }
+
+ public ProgramLocation getOpenMappedLocation(TraceLocation loc) {
+ synchronized (lock) {
+ InfoPerTrace info = requireTrackedInfo(loc.getTrace());
+ if (info == null) {
+ return null;
+ }
+ return info.getOpenMappedProgramLocation(loc.getAddress(), loc.getLifespan());
+ }
+ }
+
+ protected long getNonScratchSnap(TraceProgramView view) {
+ return view.getViewport().getTop(s -> s >= 0 ? s : null);
+ }
+
+ public ProgramLocation getStaticLocationFromDynamic(ProgramLocation loc) {
+ synchronized (lock) {
+ loc = ProgramLocationUtils.fixLocation(loc, true);
+ TraceProgramView view = (TraceProgramView) loc.getProgram();
+ Trace trace = view.getTrace();
+ TraceLocation tloc = new DefaultTraceLocation(trace, null,
+ Lifespan.at(getNonScratchSnap(view)), loc.getByteAddress());
+ ProgramLocation mapped = getOpenMappedLocation(tloc);
+ if (mapped == null) {
+ return null;
+ }
+ return ProgramLocationUtils.replaceAddress(loc, mapped.getProgram(),
+ mapped.getByteAddress());
+ }
+ }
+
+ public Set getOpenMappedLocations(ProgramLocation loc) {
+ synchronized (lock) {
+ InfoPerProgram info = requireTrackedInfo(loc.getProgram());
+ if (info == null) {
+ return null;
+ }
+ return info.getOpenMappedTraceLocations(loc.getByteAddress());
+ }
+ }
+
+ public TraceLocation getOpenMappedLocation(Trace trace, ProgramLocation loc, long snap) {
+ synchronized (lock) {
+ InfoPerProgram info = requireTrackedInfo(loc.getProgram());
+ if (info == null) {
+ return null;
+ }
+ return info.getOpenMappedTraceLocation(trace, loc.getByteAddress(), snap);
+ }
+ }
+
+ public ProgramLocation getDynamicLocationFromStatic(TraceProgramView view,
+ ProgramLocation loc) {
+ synchronized (lock) {
+ TraceLocation tloc =
+ getOpenMappedLocation(view.getTrace(), loc, getNonScratchSnap(view));
+ if (tloc == null) {
+ return null;
+ }
+ return ProgramLocationUtils.replaceAddress(loc, view, tloc.getAddress());
+ }
+ }
+
+ public Map> getOpenMappedViews(Trace trace,
+ AddressSetView set, long snap) {
+ synchronized (lock) {
+ InfoPerTrace info = requireTrackedInfo(trace);
+ if (info == null) {
+ return Map.of();
+ }
+ return info.getOpenMappedViews(set, Lifespan.at(snap));
+ }
+ }
+
+ public Map> getOpenMappedViews(Program program,
+ AddressSetView set) {
+ synchronized (lock) {
+ InfoPerProgram info = requireTrackedInfo(program);
+ if (info == null) {
+ return Map.of();
+ }
+ return info.getOpenMappedViews(set);
+ }
+ }
+
+ public Set getMappedProgramUrlsInView(Trace trace, AddressSetView set, long snap) {
+ synchronized (lock) {
+ InfoPerTrace info = requireTrackedInfo(trace);
+ if (info == null) {
+ return null;
+ }
+ return info.getMappedProgramUrlsInView(set, Lifespan.at(snap));
+ }
+ }
+}
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/DebuggerStaticMappingServicePlugin.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/DebuggerStaticMappingServicePlugin.java
index 758fee32b0..54340411c8 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/DebuggerStaticMappingServicePlugin.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/DebuggerStaticMappingServicePlugin.java
@@ -29,8 +29,8 @@ import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
import ghidra.app.plugin.core.debug.event.TraceClosedPluginEvent;
import ghidra.app.plugin.core.debug.event.TraceOpenedPluginEvent;
+import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingContext.ChangeCollector;
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingProposals.*;
-import ghidra.app.plugin.core.debug.utils.ProgramLocationUtils;
import ghidra.app.plugin.core.debug.utils.ProgramURLUtils;
import ghidra.app.services.*;
import ghidra.debug.api.modules.*;
@@ -51,7 +51,6 @@ import ghidra.trace.model.memory.TraceMemoryRegion;
import ghidra.trace.model.modules.*;
import ghidra.trace.model.program.TraceProgramView;
import ghidra.util.Msg;
-import ghidra.util.datastruct.ListenerSet;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
@@ -76,39 +75,6 @@ import ghidra.util.task.TaskMonitor;
public class DebuggerStaticMappingServicePlugin extends Plugin
implements DebuggerStaticMappingService, DomainFolderChangeListener {
- record ChangeCollector(DebuggerStaticMappingServicePlugin plugin, Set traces,
- Set programs) implements AutoCloseable {
-
- static Set subtract(Set a, Set b) {
- Set result = new HashSet<>(a);
- result.removeAll(b);
- return result;
- }
-
- public ChangeCollector(DebuggerStaticMappingServicePlugin plugin) {
- this(plugin, new HashSet<>(), new HashSet<>());
- }
-
- public void traceAffected(Trace trace) {
- this.traces.add(trace);
- }
-
- public void programAffected(Program program) {
- if (program != null) {
- this.programs.add(program);
- }
- }
-
- @Override
- public void close() {
- plugin.changeListeners.getProxy().mappingsChanged(traces, programs);
- }
- }
-
- final Map traceInfoByTrace = new HashMap<>();
- final Map programInfoByProgram = new HashMap<>();
- final Map programInfoByUrl = new HashMap<>();
-
@AutoServiceConsumed
private DebuggerTraceManagerService traceManager;
@AutoServiceConsumed
@@ -116,11 +82,8 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
@SuppressWarnings("unused")
private final AutoService.Wiring autoWiring;
- final Object lock = new Object();
-
- final ExecutorService executor = Executors.newSingleThreadExecutor();
- private final ListenerSet changeListeners =
- new ListenerSet<>(DebuggerStaticMappingChangeListener.class, true);
+ private final ExecutorService executor = Executors.newSingleThreadExecutor();
+ private final DebuggerStaticMappingContext context;
private final ProgramModuleIndexer programModuleIndexer;
private final ModuleMapProposalGenerator moduleMapProposalGenerator;
@@ -128,6 +91,7 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
public DebuggerStaticMappingServicePlugin(PluginTool tool) {
super(tool);
this.autoWiring = AutoService.wireServicesProvidedAndConsumed(this);
+ this.context = new DebuggerStaticMappingContext(executor);
this.programModuleIndexer = new ProgramModuleIndexer(tool);
this.moduleMapProposalGenerator = new ModuleMapProposalGenerator(programModuleIndexer);
}
@@ -141,28 +105,12 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
@Override
public void addChangeListener(DebuggerStaticMappingChangeListener l) {
- changeListeners.add(l);
+ context.addChangeListener(l);
}
@Override
public void removeChangeListener(DebuggerStaticMappingChangeListener l) {
- changeListeners.remove(l);
- }
-
- void checkAndClearProgram(ChangeCollector cc, MappingEntry me) {
- InfoPerProgram info = programInfoByUrl.get(me.getStaticProgramUrl());
- if (info == null) {
- return;
- }
- info.clearProgram(cc, me);
- }
-
- void checkAndFillProgram(ChangeCollector cc, MappingEntry me) {
- InfoPerProgram info = programInfoByUrl.get(me.getStaticProgramUrl());
- if (info == null) {
- return;
- }
- info.fillProgram(cc, me);
+ context.removeChangeListener(l);
}
@Override
@@ -171,99 +119,6 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
}, executor);
}
- void programsChanged() {
- try (ChangeCollector cc = new ChangeCollector(this)) {
- // Invoke change callbacks without the lock! (try must surround sync)
- synchronized (lock) {
- programsChanged(cc);
- }
- }
- }
-
- void programsChanged(ChangeCollector cc) {
- Set curProgs = Stream.of(programManager.getAllOpenPrograms())
- .filter(p -> !p.isClosed()) // Double-check
- .collect(Collectors.toSet());
- Set removed = programInfoByProgram.values()
- .stream()
- .filter(i -> !curProgs.contains(i.program) || !i.urlMatches())
- .collect(Collectors.toSet());
- processRemovedProgramInfos(cc, removed);
- Set added = ChangeCollector.subtract(curProgs, programInfoByProgram.keySet());
- processAddedPrograms(cc, added);
- }
-
- void processRemovedProgramInfos(ChangeCollector cc, Set removed) {
- for (InfoPerProgram info : removed) {
- processRemovedProgramInfo(cc, info);
- }
- }
-
- void processRemovedProgramInfo(ChangeCollector cc, InfoPerProgram info) {
- programInfoByProgram.remove(info.program);
- programInfoByUrl.remove(info.url);
- info.clearEntries(cc);
- }
-
- void processAddedPrograms(ChangeCollector cc, Set added) {
- for (Program program : added) {
- processAddedProgram(cc, program);
- }
- }
-
- void processAddedProgram(ChangeCollector cc, Program program) {
- InfoPerProgram info = new InfoPerProgram(this, program);
- programInfoByProgram.put(program, info);
- programInfoByUrl.put(info.url, info);
- info.fillEntries(cc);
- }
-
- private void tracesChanged() {
- try (ChangeCollector cc = new ChangeCollector(this)) {
- // Invoke change callbacks without the lock! (try must surround sync)
- synchronized (lock) {
- tracesChanged(cc);
- }
- }
- }
-
- void tracesChanged(ChangeCollector cc) {
- Set curTraces = traceManager.getOpenTraces()
- .stream()
- .filter(t -> !t.isClosed()) // Double-check
- .collect(Collectors.toSet());
- Set oldTraces = traceInfoByTrace.keySet();
-
- Set removed = ChangeCollector.subtract(oldTraces, curTraces);
- Set added = ChangeCollector.subtract(curTraces, oldTraces);
-
- processRemovedTraces(cc, removed);
- processAddedTraces(cc, added);
- }
-
- void processRemovedTraces(ChangeCollector cc, Set removed) {
- for (Trace trace : removed) {
- processRemovedTrace(cc, trace);
- }
- }
-
- void processRemovedTrace(ChangeCollector cc, Trace trace) {
- InfoPerTrace info = traceInfoByTrace.remove(trace);
- info.removeEntries(cc);
- }
-
- void processAddedTraces(ChangeCollector cc, Set added) {
- for (Trace trace : added) {
- processAddedTrace(cc, trace);
- }
- }
-
- void processAddedTrace(ChangeCollector cc, Trace trace) {
- InfoPerTrace info = new InfoPerTrace(this, trace);
- traceInfoByTrace.put(trace, info);
- info.resyncEntries(cc);
- }
-
@Override
public void processEvent(PluginEvent event) {
if (event instanceof ProgramOpenedPluginEvent) {
@@ -284,16 +139,35 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
public void domainFileObjectOpenedForUpdate(DomainFile file, DomainObject object) {
// This get called when a domain object is saved into the active project
// We essentially need to update the URL, which requires examining every entry
- // TODO: Could probably cut out a bit of the kruft, but this should do
+ // LATER: Could probably cut out a bit of the kruft, but this should do
if (object instanceof Program program) {
- synchronized (lock) {
- if (programInfoByProgram.containsKey(program)) {
+ synchronized (context.lock) {
+ if (context.programInfoByProgram.containsKey(program)) {
CompletableFuture.runAsync(this::programsChanged, executor);
}
}
}
}
+ void programsChanged() {
+ try (ChangeCollector cc = context.collectChanges()) {
+ Set curProgs = Stream.of(programManager.getAllOpenPrograms())
+ .filter(p -> !p.isClosed()) // Double-check
+ .collect(Collectors.toSet());
+ context.setPrograms(cc, curProgs);
+ }
+ }
+
+ private void tracesChanged() {
+ try (ChangeCollector cc = context.collectChanges()) {
+ Set curTraces = traceManager.getOpenTraces()
+ .stream()
+ .filter(t -> !t.isClosed()) // Double-check
+ .collect(Collectors.toSet());
+ context.setTraces(cc, curTraces);
+ }
+ }
+
@Override
public void addMapping(TraceLocation from, ProgramLocation to, long length,
boolean truncateExisting) throws TraceConflictedMappingException {
@@ -380,151 +254,10 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
addMappings(entries, monitor, truncateExisting, "Add regions mappings");
}
- protected T noTraceInfo() {
- Msg.debug(this, "The given trace is not open in this tool " +
- "(or the service hasn't received and processed the open-trace event, yet)");
- return null;
- }
-
- protected T noProgramInfo() {
- Msg.debug(this, "The given program is not open in this tool " +
- "(or the service hasn't received and processed the open-program event, yet)");
- return null;
- }
-
- protected T noProject() {
- return DebuggerStaticMappingUtils.noProject(this);
- }
-
- protected InfoPerTrace requireTrackedInfo(Trace trace) {
- InfoPerTrace info = traceInfoByTrace.get(trace);
- if (info == null) {
- return noTraceInfo();
- }
- return info;
- }
-
- protected InfoPerProgram requireTrackedInfo(Program program) {
- InfoPerProgram info = programInfoByProgram.get(program);
- if (info == null) {
- return noProgramInfo();
- }
- return info;
- }
-
- @Override
- public Set getOpenMappedProgramsAtSnap(Trace trace, long snap) {
- synchronized (lock) {
- InfoPerTrace info = requireTrackedInfo(trace);
- if (info == null) {
- return null;
- }
- return info.getOpenMappedProgramsAtSnap(snap);
- }
- }
-
- @Override
- public ProgramLocation getOpenMappedLocation(TraceLocation loc) {
- synchronized (lock) {
- InfoPerTrace info = requireTrackedInfo(loc.getTrace());
- if (info == null) {
- return null;
- }
- return info.getOpenMappedProgramLocation(loc.getAddress(), loc.getLifespan());
- }
- }
-
- protected long getNonScratchSnap(TraceProgramView view) {
- return view.getViewport().getTop(s -> s >= 0 ? s : null);
- }
-
- @Override
- public ProgramLocation getStaticLocationFromDynamic(ProgramLocation loc) {
- synchronized (lock) {
- loc = ProgramLocationUtils.fixLocation(loc, true);
- TraceProgramView view = (TraceProgramView) loc.getProgram();
- Trace trace = view.getTrace();
- TraceLocation tloc = new DefaultTraceLocation(trace, null,
- Lifespan.at(getNonScratchSnap(view)), loc.getByteAddress());
- ProgramLocation mapped = getOpenMappedLocation(tloc);
- if (mapped == null) {
- return null;
- }
- return ProgramLocationUtils.replaceAddress(loc, mapped.getProgram(),
- mapped.getByteAddress());
- }
- }
-
- @Override
- public Set getOpenMappedLocations(ProgramLocation loc) {
- synchronized (lock) {
- InfoPerProgram info = requireTrackedInfo(loc.getProgram());
- if (info == null) {
- return null;
- }
- return info.getOpenMappedTraceLocations(loc.getByteAddress());
- }
- }
-
- @Override
- public TraceLocation getOpenMappedLocation(Trace trace, ProgramLocation loc, long snap) {
- synchronized (lock) {
- InfoPerProgram info = requireTrackedInfo(loc.getProgram());
- if (info == null) {
- return null;
- }
- return info.getOpenMappedTraceLocation(trace, loc.getByteAddress(), snap);
- }
- }
-
- @Override
- public ProgramLocation getDynamicLocationFromStatic(TraceProgramView view,
- ProgramLocation loc) {
- synchronized (lock) {
- TraceLocation tloc =
- getOpenMappedLocation(view.getTrace(), loc, getNonScratchSnap(view));
- if (tloc == null) {
- return null;
- }
- return ProgramLocationUtils.replaceAddress(loc, view, tloc.getAddress());
- }
- }
-
- @Override
- public Map> getOpenMappedViews(Trace trace,
- AddressSetView set, long snap) {
- synchronized (lock) {
- InfoPerTrace info = requireTrackedInfo(trace);
- if (info == null) {
- return Map.of();
- }
- return info.getOpenMappedViews(set, Lifespan.at(snap));
- }
- }
-
- @Override
- public Map> getOpenMappedViews(Program program,
- AddressSetView set) {
- synchronized (lock) {
- InfoPerProgram info = requireTrackedInfo(program);
- if (info == null) {
- return Map.of();
- }
- return info.getOpenMappedViews(set);
- }
- }
-
@Override
public Set openMappedProgramsInView(Trace trace, AddressSetView set, long snap,
Set failures) {
- Set urls;
- synchronized (lock) {
- InfoPerTrace info = requireTrackedInfo(trace);
- if (info == null) {
- return null;
- }
- urls = info.getMappedProgramUrlsInView(set, Lifespan.at(snap));
- }
+ Set urls = context.getMappedProgramUrlsInView(trace, set, snap);
Set result = new HashSet<>();
for (URL url : urls) {
try {
@@ -545,6 +278,49 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
return result;
}
+ @Override
+ public Set getOpenMappedProgramsAtSnap(Trace trace, long snap) {
+ return context.getOpenMappedProgramsAtSnap(trace, snap);
+ }
+
+ @Override
+ public ProgramLocation getOpenMappedLocation(TraceLocation loc) {
+ return context.getOpenMappedLocation(loc);
+ }
+
+ @Override
+ public ProgramLocation getStaticLocationFromDynamic(ProgramLocation loc) {
+ return context.getStaticLocationFromDynamic(loc);
+ }
+
+ @Override
+ public Set getOpenMappedLocations(ProgramLocation loc) {
+ return context.getOpenMappedLocations(loc);
+ }
+
+ @Override
+ public TraceLocation getOpenMappedLocation(Trace trace, ProgramLocation loc, long snap) {
+ return context.getOpenMappedLocation(trace, loc, snap);
+ }
+
+ @Override
+ public ProgramLocation getDynamicLocationFromStatic(TraceProgramView view,
+ ProgramLocation loc) {
+ return context.getDynamicLocationFromStatic(view, loc);
+ }
+
+ @Override
+ public Map> getOpenMappedViews(Trace trace,
+ AddressSetView set, long snap) {
+ return context.getOpenMappedViews(trace, set, snap);
+ }
+
+ @Override
+ public Map> getOpenMappedViews(Program program,
+ AddressSetView set) {
+ return context.getOpenMappedViews(program, set);
+ }
+
protected Collection extends Program> orderCurrentFirst(
Collection extends Program> programs) {
if (programManager == null) {
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/DynamicStaticSynchronizationPlugin.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/DynamicStaticSynchronizationPlugin.java
index 4a40d1132f..ba5a5b7350 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/DynamicStaticSynchronizationPlugin.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/DynamicStaticSynchronizationPlugin.java
@@ -35,7 +35,6 @@ import ghidra.app.plugin.core.debug.event.*;
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
import ghidra.app.plugin.core.debug.utils.ProgramURLUtils;
import ghidra.app.services.*;
-import ghidra.app.services.DebuggerStaticMappingService.MappedAddressRange;
import ghidra.debug.api.modules.*;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.framework.model.DomainFile;
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/InfoPerProgram.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/InfoPerProgram.java
index 330f2d9cf5..c7995d5b47 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/InfoPerProgram.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/InfoPerProgram.java
@@ -19,9 +19,9 @@ import java.net.URL;
import java.util.*;
import java.util.concurrent.CompletableFuture;
-import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingServicePlugin.ChangeCollector;
+import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingContext.ChangeCollector;
import ghidra.app.plugin.core.debug.utils.ProgramURLUtils;
-import ghidra.app.services.DebuggerStaticMappingService.MappedAddressRange;
+import ghidra.debug.api.modules.MappedAddressRange;
import ghidra.framework.model.*;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
@@ -52,14 +52,14 @@ class InfoPerProgram implements DomainObjectListener {
}
}
- private final DebuggerStaticMappingServicePlugin plugin;
+ private final DebuggerStaticMappingContext ctx;
final Program program;
final NavMultiMap inboundByStaticAddress = new NavMultiMap<>();
final URL url;
- InfoPerProgram(DebuggerStaticMappingServicePlugin plugin, Program program) {
- this.plugin = plugin;
+ InfoPerProgram(DebuggerStaticMappingContext ctx, Program program) {
+ this.ctx = ctx;
this.program = program;
this.url = ProgramURLUtils.getUrlFromProgram(program);
@@ -70,7 +70,12 @@ class InfoPerProgram implements DomainObjectListener {
public void domainObjectChanged(DomainObjectChangedEvent ev) {
if (ev.contains(DomainObjectEvent.FILE_CHANGED) || ev.contains(DomainObjectEvent.RENAMED)) {
if (!urlMatches()) {
- CompletableFuture.runAsync(plugin::programsChanged, plugin.executor);
+ CompletableFuture.runAsync(() -> {
+ try (ChangeCollector cc = ctx.collectChanges()) {
+ ctx.processRemovedProgramInfo(cc, this);
+ ctx.processAddedProgram(cc, program);
+ }
+ }, ctx.executor);
}
}
}
@@ -95,7 +100,7 @@ class InfoPerProgram implements DomainObjectListener {
if (url == null) {
return;
}
- for (InfoPerTrace info : plugin.traceInfoByTrace.values()) {
+ for (InfoPerTrace info : ctx.traceInfoByTrace.values()) {
info.clearEntriesForProgram(cc, this);
}
}
@@ -104,7 +109,7 @@ class InfoPerProgram implements DomainObjectListener {
if (url == null) {
return;
}
- for (InfoPerTrace info : plugin.traceInfoByTrace.values()) {
+ for (InfoPerTrace info : ctx.traceInfoByTrace.values()) {
info.fillEntriesForProgram(cc, this);
}
}
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/InfoPerTrace.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/InfoPerTrace.java
index 7f3378d2e9..84bb5f7d32 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/InfoPerTrace.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/InfoPerTrace.java
@@ -24,8 +24,8 @@ import java.util.stream.Collectors;
import org.apache.commons.collections4.MultiValuedMap;
import org.apache.commons.collections4.multimap.HashSetValuedHashMap;
-import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingServicePlugin.ChangeCollector;
-import ghidra.app.services.DebuggerStaticMappingService.MappedAddressRange;
+import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingContext.ChangeCollector;
+import ghidra.debug.api.modules.MappedAddressRange;
import ghidra.framework.model.DomainObjectChangedEvent;
import ghidra.framework.model.DomainObjectEvent;
import ghidra.program.model.address.*;
@@ -37,7 +37,7 @@ import ghidra.trace.util.TraceEvents;
import ghidra.util.Msg;
class InfoPerTrace extends TraceDomainObjectListener {
- private final DebuggerStaticMappingServicePlugin plugin;
+ private final DebuggerStaticMappingContext ctx;
final Trace trace;
final Map outboundByEntry = new HashMap<>();
@@ -47,8 +47,8 @@ class InfoPerTrace extends TraceDomainObjectListener {
private volatile boolean needsResync = false;
- InfoPerTrace(DebuggerStaticMappingServicePlugin plugin, Trace trace) {
- this.plugin = plugin;
+ InfoPerTrace(DebuggerStaticMappingContext ctx, Trace trace) {
+ this.ctx = ctx;
this.trace = trace;
listenForUntyped(DomainObjectEvent.RESTORED, e -> objectRestored());
@@ -63,7 +63,7 @@ class InfoPerTrace extends TraceDomainObjectListener {
// Now do the actual processing
if (needsResync) {
needsResync = false;
- CompletableFuture.runAsync(this::resyncEntries, plugin.executor);
+ CompletableFuture.runAsync(this::resyncEntries, ctx.executor);
}
}
@@ -84,9 +84,9 @@ class InfoPerTrace extends TraceDomainObjectListener {
}
private void resyncEntries() {
- try (ChangeCollector cc = new ChangeCollector(plugin)) {
+ try (ChangeCollector cc = new ChangeCollector(ctx)) {
// Invoke change callbacks without the lock! (try must surround sync)
- synchronized (plugin.lock) {
+ synchronized (ctx.lock) {
resyncEntries(cc);
}
}
@@ -124,7 +124,7 @@ class InfoPerTrace extends TraceDomainObjectListener {
}
outboundByRange.remove(me.getTraceAddressSnapRange());
outboundByStaticUrl.removeMapping(me.getStaticProgramUrl(), me);
- plugin.checkAndClearProgram(cc, me);
+ ctx.checkAndClearProgram(cc, me);
}
private void processAddedEntries(ChangeCollector cc, Set added) {
@@ -138,7 +138,7 @@ class InfoPerTrace extends TraceDomainObjectListener {
outboundByEntry.put(entry, me);
outboundByRange.put(me.getTraceAddressSnapRange(), me);
outboundByStaticUrl.put(me.getStaticProgramUrl(), me);
- plugin.checkAndFillProgram(cc, me);
+ ctx.checkAndFillProgram(cc, me);
}
void clearEntriesForProgram(ChangeCollector cc, InfoPerProgram progInfo) {
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/MappingEntry.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/MappingEntry.java
index d551c0b7cb..b9d4d537c4 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/MappingEntry.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/MappingEntry.java
@@ -18,7 +18,7 @@ package ghidra.app.plugin.core.debug.service.modules;
import java.net.URL;
import java.util.Objects;
-import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingServicePlugin.ChangeCollector;
+import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingContext.ChangeCollector;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.program.util.ProgramLocation;
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/target/AbstractTarget.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/target/AbstractTarget.java
index 962b42280a..1fa5fcf2d6 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/target/AbstractTarget.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/target/AbstractTarget.java
@@ -24,8 +24,8 @@ import docking.ActionContext;
import ghidra.app.context.NavigatableActionContext;
import ghidra.app.nav.Navigatable;
import ghidra.app.services.*;
-import ghidra.app.services.DebuggerStaticMappingService.MappedAddressRange;
import ghidra.async.AsyncUtils;
+import ghidra.debug.api.modules.MappedAddressRange;
import ghidra.debug.api.target.Target;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.framework.plugintool.PluginTool;
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/utils/AbstractMappedMemoryBytesVisitor.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/utils/AbstractMappedMemoryBytesVisitor.java
index 137780eef9..f31b5345bc 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/utils/AbstractMappedMemoryBytesVisitor.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/utils/AbstractMappedMemoryBytesVisitor.java
@@ -4,9 +4,9 @@
* 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.
@@ -20,7 +20,7 @@ import java.util.Map.Entry;
import java.util.Objects;
import ghidra.app.services.DebuggerStaticMappingService;
-import ghidra.app.services.DebuggerStaticMappingService.MappedAddressRange;
+import ghidra.debug.api.modules.MappedAddressRange;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.*;
diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/modules/DebuggerStaticMappingServiceTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/modules/DebuggerStaticMappingServiceTest.java
index 319205bcfa..7eef9ad20b 100644
--- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/modules/DebuggerStaticMappingServiceTest.java
+++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/modules/DebuggerStaticMappingServiceTest.java
@@ -27,7 +27,7 @@ import db.Transaction;
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerTest;
import ghidra.app.plugin.core.debug.gui.modules.DebuggerModulesProviderTest;
import ghidra.app.services.DebuggerStaticMappingService;
-import ghidra.app.services.DebuggerStaticMappingService.MappedAddressRange;
+import ghidra.debug.api.modules.MappedAddressRange;
import ghidra.framework.model.DomainFile;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/async/AsyncUtils.java b/Ghidra/Framework/Generic/src/main/java/ghidra/async/AsyncUtils.java
index c75d358404..d3b169d6f1 100644
--- a/Ghidra/Framework/Generic/src/main/java/ghidra/async/AsyncUtils.java
+++ b/Ghidra/Framework/Generic/src/main/java/ghidra/async/AsyncUtils.java
@@ -29,6 +29,12 @@ public interface AsyncUtils {
ExecutorService FRAMEWORK_EXECUTOR = Executors.newWorkStealingPool();
ExecutorService SWING_EXECUTOR = SwingExecutorService.LATER;
+ Executor DIRECT_EXECUTOR = new Executor() {
+ @Override
+ public void execute(Runnable command) {
+ command.run();
+ }
+ };
// NB. This was a bad idea, because CFs may maintain refs to dependents.
//CompletableFuture NIL = CompletableFuture.completedFuture(null);