mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-28 09:51:37 +08:00
Merge remote-tracking branch 'origin/GP-6680_Dan_separateMapperVsService--SQUASHED'
This commit is contained in:
+2
-146
@@ -23,7 +23,8 @@ import ghidra.debug.api.modules.ModuleMapProposal.ModuleMapEntry;
|
|||||||
import ghidra.debug.api.modules.RegionMapProposal.RegionMapEntry;
|
import ghidra.debug.api.modules.RegionMapProposal.RegionMapEntry;
|
||||||
import ghidra.debug.api.modules.SectionMapProposal.SectionMapEntry;
|
import ghidra.debug.api.modules.SectionMapProposal.SectionMapEntry;
|
||||||
import ghidra.framework.model.DomainFile;
|
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.listing.Program;
|
||||||
import ghidra.program.model.mem.MemoryBlock;
|
import ghidra.program.model.mem.MemoryBlock;
|
||||||
import ghidra.program.util.ProgramLocation;
|
import ghidra.program.util.ProgramLocation;
|
||||||
@@ -50,151 +51,6 @@ import ghidra.util.task.TaskMonitor;
|
|||||||
*/
|
*/
|
||||||
public interface DebuggerStaticMappingService {
|
public interface DebuggerStaticMappingService {
|
||||||
|
|
||||||
/**
|
|
||||||
* A pair for describing sets of mapped addresses
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* Note, the natural order is by the <em>destination</em> address.
|
|
||||||
*/
|
|
||||||
public class MappedAddressRange implements Comparable<MappedAddressRange> {
|
|
||||||
|
|
||||||
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 "<MappedRange " + srcRange + "::" + dstRange + ">";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the shift from the source address range to this address range
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* 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
|
* Add a static mapping (relocation) from the given trace to the given program
|
||||||
*
|
*
|
||||||
|
|||||||
+165
@@ -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
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Note, the natural order is by the <em>destination</em> address.
|
||||||
|
*/
|
||||||
|
public class MappedAddressRange implements Comparable<MappedAddressRange> {
|
||||||
|
|
||||||
|
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 "<MappedRange " + srcRange + "::" + dstRange + ">";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the shift from the source address range to this address range
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -32,7 +32,7 @@ import docking.widgets.table.*;
|
|||||||
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
|
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
|
||||||
import ghidra.app.plugin.core.debug.gui.copying.DebuggerCopyPlan.Copier;
|
import ghidra.app.plugin.core.debug.gui.copying.DebuggerCopyPlan.Copier;
|
||||||
import ghidra.app.services.*;
|
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.target.Target;
|
||||||
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
|
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
|
|||||||
+1
-1
@@ -20,8 +20,8 @@ import java.util.concurrent.CompletableFuture;
|
|||||||
|
|
||||||
import ghidra.app.plugin.core.debug.utils.AbstractMappedMemoryBytesVisitor;
|
import ghidra.app.plugin.core.debug.utils.AbstractMappedMemoryBytesVisitor;
|
||||||
import ghidra.app.services.DebuggerStaticMappingService;
|
import ghidra.app.services.DebuggerStaticMappingService;
|
||||||
import ghidra.app.services.DebuggerStaticMappingService.MappedAddressRange;
|
|
||||||
import ghidra.debug.api.emulation.PcodeDebuggerMemoryAccess;
|
import ghidra.debug.api.emulation.PcodeDebuggerMemoryAccess;
|
||||||
|
import ghidra.debug.api.modules.MappedAddressRange;
|
||||||
import ghidra.debug.api.target.Target;
|
import ghidra.debug.api.target.Target;
|
||||||
import ghidra.framework.plugintool.ServiceProvider;
|
import ghidra.framework.plugintool.ServiceProvider;
|
||||||
import ghidra.pcode.exec.PcodeExecutorStatePiece;
|
import ghidra.pcode.exec.PcodeExecutorStatePiece;
|
||||||
|
|||||||
+343
@@ -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<Trace> traces,
|
||||||
|
Set<Program> programs) implements AutoCloseable {
|
||||||
|
|
||||||
|
static <T> Set<T> subtract(Set<T> a, Set<T> b) {
|
||||||
|
Set<T> 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<Trace, InfoPerTrace> traceInfoByTrace = new HashMap<>();
|
||||||
|
final Map<Program, InfoPerProgram> programInfoByProgram = new HashMap<>();
|
||||||
|
final Map<URL, InfoPerProgram> programInfoByUrl = new HashMap<>();
|
||||||
|
|
||||||
|
final Object lock = new Object();
|
||||||
|
|
||||||
|
final Executor executor;
|
||||||
|
private final ListenerSet<DebuggerStaticMappingChangeListener> 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<Program> programs) {
|
||||||
|
synchronized (lock) {
|
||||||
|
Set<InfoPerProgram> removed = programInfoByProgram.values()
|
||||||
|
.stream()
|
||||||
|
.filter(i -> !programs.contains(i.program) || !i.urlMatches())
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
processRemovedProgramInfos(cc, removed);
|
||||||
|
Set<Program> 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<Trace> traces) {
|
||||||
|
synchronized (lock) {
|
||||||
|
Set<Trace> oldTraces = traceInfoByTrace.keySet();
|
||||||
|
|
||||||
|
Set<Trace> removed = ChangeCollector.subtract(oldTraces, traces);
|
||||||
|
Set<Trace> added = ChangeCollector.subtract(traces, oldTraces);
|
||||||
|
|
||||||
|
processRemovedTraces(cc, removed);
|
||||||
|
processAddedTraces(cc, added);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected <T> 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> 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> 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<InfoPerProgram> 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<Program> 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<Trace> 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<Trace> 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<Program> 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<TraceLocation> 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<Program, Collection<MappedAddressRange>> 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<TraceSpan, Collection<MappedAddressRange>> getOpenMappedViews(Program program,
|
||||||
|
AddressSetView set) {
|
||||||
|
synchronized (lock) {
|
||||||
|
InfoPerProgram info = requireTrackedInfo(program);
|
||||||
|
if (info == null) {
|
||||||
|
return Map.of();
|
||||||
|
}
|
||||||
|
return info.getOpenMappedViews(set);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<URL> 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+72
-296
@@ -29,8 +29,8 @@ import ghidra.app.plugin.PluginCategoryNames;
|
|||||||
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
|
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
|
||||||
import ghidra.app.plugin.core.debug.event.TraceClosedPluginEvent;
|
import ghidra.app.plugin.core.debug.event.TraceClosedPluginEvent;
|
||||||
import ghidra.app.plugin.core.debug.event.TraceOpenedPluginEvent;
|
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.service.modules.DebuggerStaticMappingProposals.*;
|
||||||
import ghidra.app.plugin.core.debug.utils.ProgramLocationUtils;
|
|
||||||
import ghidra.app.plugin.core.debug.utils.ProgramURLUtils;
|
import ghidra.app.plugin.core.debug.utils.ProgramURLUtils;
|
||||||
import ghidra.app.services.*;
|
import ghidra.app.services.*;
|
||||||
import ghidra.debug.api.modules.*;
|
import ghidra.debug.api.modules.*;
|
||||||
@@ -51,7 +51,6 @@ import ghidra.trace.model.memory.TraceMemoryRegion;
|
|||||||
import ghidra.trace.model.modules.*;
|
import ghidra.trace.model.modules.*;
|
||||||
import ghidra.trace.model.program.TraceProgramView;
|
import ghidra.trace.model.program.TraceProgramView;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.datastruct.ListenerSet;
|
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
@@ -76,39 +75,6 @@ import ghidra.util.task.TaskMonitor;
|
|||||||
public class DebuggerStaticMappingServicePlugin extends Plugin
|
public class DebuggerStaticMappingServicePlugin extends Plugin
|
||||||
implements DebuggerStaticMappingService, DomainFolderChangeListener {
|
implements DebuggerStaticMappingService, DomainFolderChangeListener {
|
||||||
|
|
||||||
record ChangeCollector(DebuggerStaticMappingServicePlugin plugin, Set<Trace> traces,
|
|
||||||
Set<Program> programs) implements AutoCloseable {
|
|
||||||
|
|
||||||
static <T> Set<T> subtract(Set<T> a, Set<T> b) {
|
|
||||||
Set<T> 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<Trace, InfoPerTrace> traceInfoByTrace = new HashMap<>();
|
|
||||||
final Map<Program, InfoPerProgram> programInfoByProgram = new HashMap<>();
|
|
||||||
final Map<URL, InfoPerProgram> programInfoByUrl = new HashMap<>();
|
|
||||||
|
|
||||||
@AutoServiceConsumed
|
@AutoServiceConsumed
|
||||||
private DebuggerTraceManagerService traceManager;
|
private DebuggerTraceManagerService traceManager;
|
||||||
@AutoServiceConsumed
|
@AutoServiceConsumed
|
||||||
@@ -116,11 +82,8 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
|
|||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
private final AutoService.Wiring autoWiring;
|
private final AutoService.Wiring autoWiring;
|
||||||
|
|
||||||
final Object lock = new Object();
|
private final ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||||
|
private final DebuggerStaticMappingContext context;
|
||||||
final ExecutorService executor = Executors.newSingleThreadExecutor();
|
|
||||||
private final ListenerSet<DebuggerStaticMappingChangeListener> changeListeners =
|
|
||||||
new ListenerSet<>(DebuggerStaticMappingChangeListener.class, true);
|
|
||||||
|
|
||||||
private final ProgramModuleIndexer programModuleIndexer;
|
private final ProgramModuleIndexer programModuleIndexer;
|
||||||
private final ModuleMapProposalGenerator moduleMapProposalGenerator;
|
private final ModuleMapProposalGenerator moduleMapProposalGenerator;
|
||||||
@@ -128,6 +91,7 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
|
|||||||
public DebuggerStaticMappingServicePlugin(PluginTool tool) {
|
public DebuggerStaticMappingServicePlugin(PluginTool tool) {
|
||||||
super(tool);
|
super(tool);
|
||||||
this.autoWiring = AutoService.wireServicesProvidedAndConsumed(this);
|
this.autoWiring = AutoService.wireServicesProvidedAndConsumed(this);
|
||||||
|
this.context = new DebuggerStaticMappingContext(executor);
|
||||||
this.programModuleIndexer = new ProgramModuleIndexer(tool);
|
this.programModuleIndexer = new ProgramModuleIndexer(tool);
|
||||||
this.moduleMapProposalGenerator = new ModuleMapProposalGenerator(programModuleIndexer);
|
this.moduleMapProposalGenerator = new ModuleMapProposalGenerator(programModuleIndexer);
|
||||||
}
|
}
|
||||||
@@ -141,28 +105,12 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addChangeListener(DebuggerStaticMappingChangeListener l) {
|
public void addChangeListener(DebuggerStaticMappingChangeListener l) {
|
||||||
changeListeners.add(l);
|
context.addChangeListener(l);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeChangeListener(DebuggerStaticMappingChangeListener l) {
|
public void removeChangeListener(DebuggerStaticMappingChangeListener l) {
|
||||||
changeListeners.remove(l);
|
context.removeChangeListener(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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -171,99 +119,6 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
|
|||||||
}, executor);
|
}, 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<Program> curProgs = Stream.of(programManager.getAllOpenPrograms())
|
|
||||||
.filter(p -> !p.isClosed()) // Double-check
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
Set<InfoPerProgram> removed = programInfoByProgram.values()
|
|
||||||
.stream()
|
|
||||||
.filter(i -> !curProgs.contains(i.program) || !i.urlMatches())
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
processRemovedProgramInfos(cc, removed);
|
|
||||||
Set<Program> added = ChangeCollector.subtract(curProgs, programInfoByProgram.keySet());
|
|
||||||
processAddedPrograms(cc, added);
|
|
||||||
}
|
|
||||||
|
|
||||||
void processRemovedProgramInfos(ChangeCollector cc, Set<InfoPerProgram> 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<Program> 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<Trace> curTraces = traceManager.getOpenTraces()
|
|
||||||
.stream()
|
|
||||||
.filter(t -> !t.isClosed()) // Double-check
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
Set<Trace> oldTraces = traceInfoByTrace.keySet();
|
|
||||||
|
|
||||||
Set<Trace> removed = ChangeCollector.subtract(oldTraces, curTraces);
|
|
||||||
Set<Trace> added = ChangeCollector.subtract(curTraces, oldTraces);
|
|
||||||
|
|
||||||
processRemovedTraces(cc, removed);
|
|
||||||
processAddedTraces(cc, added);
|
|
||||||
}
|
|
||||||
|
|
||||||
void processRemovedTraces(ChangeCollector cc, Set<Trace> 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<Trace> 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
|
@Override
|
||||||
public void processEvent(PluginEvent event) {
|
public void processEvent(PluginEvent event) {
|
||||||
if (event instanceof ProgramOpenedPluginEvent) {
|
if (event instanceof ProgramOpenedPluginEvent) {
|
||||||
@@ -284,16 +139,35 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
|
|||||||
public void domainFileObjectOpenedForUpdate(DomainFile file, DomainObject object) {
|
public void domainFileObjectOpenedForUpdate(DomainFile file, DomainObject object) {
|
||||||
// This get called when a domain object is saved into the active project
|
// 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
|
// 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) {
|
if (object instanceof Program program) {
|
||||||
synchronized (lock) {
|
synchronized (context.lock) {
|
||||||
if (programInfoByProgram.containsKey(program)) {
|
if (context.programInfoByProgram.containsKey(program)) {
|
||||||
CompletableFuture.runAsync(this::programsChanged, executor);
|
CompletableFuture.runAsync(this::programsChanged, executor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void programsChanged() {
|
||||||
|
try (ChangeCollector cc = context.collectChanges()) {
|
||||||
|
Set<Program> 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<Trace> curTraces = traceManager.getOpenTraces()
|
||||||
|
.stream()
|
||||||
|
.filter(t -> !t.isClosed()) // Double-check
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
context.setTraces(cc, curTraces);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addMapping(TraceLocation from, ProgramLocation to, long length,
|
public void addMapping(TraceLocation from, ProgramLocation to, long length,
|
||||||
boolean truncateExisting) throws TraceConflictedMappingException {
|
boolean truncateExisting) throws TraceConflictedMappingException {
|
||||||
@@ -380,151 +254,10 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
|
|||||||
addMappings(entries, monitor, truncateExisting, "Add regions mappings");
|
addMappings(entries, monitor, truncateExisting, "Add regions mappings");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected <T> 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> 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> 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<Program> 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<TraceLocation> 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<Program, Collection<MappedAddressRange>> 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<TraceSpan, Collection<MappedAddressRange>> getOpenMappedViews(Program program,
|
|
||||||
AddressSetView set) {
|
|
||||||
synchronized (lock) {
|
|
||||||
InfoPerProgram info = requireTrackedInfo(program);
|
|
||||||
if (info == null) {
|
|
||||||
return Map.of();
|
|
||||||
}
|
|
||||||
return info.getOpenMappedViews(set);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<Program> openMappedProgramsInView(Trace trace, AddressSetView set, long snap,
|
public Set<Program> openMappedProgramsInView(Trace trace, AddressSetView set, long snap,
|
||||||
Set<Exception> failures) {
|
Set<Exception> failures) {
|
||||||
Set<URL> urls;
|
Set<URL> urls = context.getMappedProgramUrlsInView(trace, set, snap);
|
||||||
synchronized (lock) {
|
|
||||||
InfoPerTrace info = requireTrackedInfo(trace);
|
|
||||||
if (info == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
urls = info.getMappedProgramUrlsInView(set, Lifespan.at(snap));
|
|
||||||
}
|
|
||||||
Set<Program> result = new HashSet<>();
|
Set<Program> result = new HashSet<>();
|
||||||
for (URL url : urls) {
|
for (URL url : urls) {
|
||||||
try {
|
try {
|
||||||
@@ -545,6 +278,49 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Program> 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<TraceLocation> 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<Program, Collection<MappedAddressRange>> getOpenMappedViews(Trace trace,
|
||||||
|
AddressSetView set, long snap) {
|
||||||
|
return context.getOpenMappedViews(trace, set, snap);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<TraceSpan, Collection<MappedAddressRange>> getOpenMappedViews(Program program,
|
||||||
|
AddressSetView set) {
|
||||||
|
return context.getOpenMappedViews(program, set);
|
||||||
|
}
|
||||||
|
|
||||||
protected Collection<? extends Program> orderCurrentFirst(
|
protected Collection<? extends Program> orderCurrentFirst(
|
||||||
Collection<? extends Program> programs) {
|
Collection<? extends Program> programs) {
|
||||||
if (programManager == null) {
|
if (programManager == null) {
|
||||||
|
|||||||
-1
@@ -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.gui.DebuggerResources;
|
||||||
import ghidra.app.plugin.core.debug.utils.ProgramURLUtils;
|
import ghidra.app.plugin.core.debug.utils.ProgramURLUtils;
|
||||||
import ghidra.app.services.*;
|
import ghidra.app.services.*;
|
||||||
import ghidra.app.services.DebuggerStaticMappingService.MappedAddressRange;
|
|
||||||
import ghidra.debug.api.modules.*;
|
import ghidra.debug.api.modules.*;
|
||||||
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
|
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
|
||||||
import ghidra.framework.model.DomainFile;
|
import ghidra.framework.model.DomainFile;
|
||||||
|
|||||||
+13
-8
@@ -19,9 +19,9 @@ import java.net.URL;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.CompletableFuture;
|
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.plugin.core.debug.utils.ProgramURLUtils;
|
||||||
import ghidra.app.services.DebuggerStaticMappingService.MappedAddressRange;
|
import ghidra.debug.api.modules.MappedAddressRange;
|
||||||
import ghidra.framework.model.*;
|
import ghidra.framework.model.*;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.listing.Program;
|
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 Program program;
|
||||||
final NavMultiMap<Address, MappingEntry> inboundByStaticAddress = new NavMultiMap<>();
|
final NavMultiMap<Address, MappingEntry> inboundByStaticAddress = new NavMultiMap<>();
|
||||||
|
|
||||||
final URL url;
|
final URL url;
|
||||||
|
|
||||||
InfoPerProgram(DebuggerStaticMappingServicePlugin plugin, Program program) {
|
InfoPerProgram(DebuggerStaticMappingContext ctx, Program program) {
|
||||||
this.plugin = plugin;
|
this.ctx = ctx;
|
||||||
this.program = program;
|
this.program = program;
|
||||||
this.url = ProgramURLUtils.getUrlFromProgram(program);
|
this.url = ProgramURLUtils.getUrlFromProgram(program);
|
||||||
|
|
||||||
@@ -70,7 +70,12 @@ class InfoPerProgram implements DomainObjectListener {
|
|||||||
public void domainObjectChanged(DomainObjectChangedEvent ev) {
|
public void domainObjectChanged(DomainObjectChangedEvent ev) {
|
||||||
if (ev.contains(DomainObjectEvent.FILE_CHANGED) || ev.contains(DomainObjectEvent.RENAMED)) {
|
if (ev.contains(DomainObjectEvent.FILE_CHANGED) || ev.contains(DomainObjectEvent.RENAMED)) {
|
||||||
if (!urlMatches()) {
|
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) {
|
if (url == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (InfoPerTrace info : plugin.traceInfoByTrace.values()) {
|
for (InfoPerTrace info : ctx.traceInfoByTrace.values()) {
|
||||||
info.clearEntriesForProgram(cc, this);
|
info.clearEntriesForProgram(cc, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -104,7 +109,7 @@ class InfoPerProgram implements DomainObjectListener {
|
|||||||
if (url == null) {
|
if (url == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (InfoPerTrace info : plugin.traceInfoByTrace.values()) {
|
for (InfoPerTrace info : ctx.traceInfoByTrace.values()) {
|
||||||
info.fillEntriesForProgram(cc, this);
|
info.fillEntriesForProgram(cc, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-10
@@ -24,8 +24,8 @@ import java.util.stream.Collectors;
|
|||||||
import org.apache.commons.collections4.MultiValuedMap;
|
import org.apache.commons.collections4.MultiValuedMap;
|
||||||
import org.apache.commons.collections4.multimap.HashSetValuedHashMap;
|
import org.apache.commons.collections4.multimap.HashSetValuedHashMap;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingServicePlugin.ChangeCollector;
|
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingContext.ChangeCollector;
|
||||||
import ghidra.app.services.DebuggerStaticMappingService.MappedAddressRange;
|
import ghidra.debug.api.modules.MappedAddressRange;
|
||||||
import ghidra.framework.model.DomainObjectChangedEvent;
|
import ghidra.framework.model.DomainObjectChangedEvent;
|
||||||
import ghidra.framework.model.DomainObjectEvent;
|
import ghidra.framework.model.DomainObjectEvent;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
@@ -37,7 +37,7 @@ import ghidra.trace.util.TraceEvents;
|
|||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
|
|
||||||
class InfoPerTrace extends TraceDomainObjectListener {
|
class InfoPerTrace extends TraceDomainObjectListener {
|
||||||
private final DebuggerStaticMappingServicePlugin plugin;
|
private final DebuggerStaticMappingContext ctx;
|
||||||
final Trace trace;
|
final Trace trace;
|
||||||
|
|
||||||
final Map<TraceStaticMapping, MappingEntry> outboundByEntry = new HashMap<>();
|
final Map<TraceStaticMapping, MappingEntry> outboundByEntry = new HashMap<>();
|
||||||
@@ -47,8 +47,8 @@ class InfoPerTrace extends TraceDomainObjectListener {
|
|||||||
|
|
||||||
private volatile boolean needsResync = false;
|
private volatile boolean needsResync = false;
|
||||||
|
|
||||||
InfoPerTrace(DebuggerStaticMappingServicePlugin plugin, Trace trace) {
|
InfoPerTrace(DebuggerStaticMappingContext ctx, Trace trace) {
|
||||||
this.plugin = plugin;
|
this.ctx = ctx;
|
||||||
this.trace = trace;
|
this.trace = trace;
|
||||||
|
|
||||||
listenForUntyped(DomainObjectEvent.RESTORED, e -> objectRestored());
|
listenForUntyped(DomainObjectEvent.RESTORED, e -> objectRestored());
|
||||||
@@ -63,7 +63,7 @@ class InfoPerTrace extends TraceDomainObjectListener {
|
|||||||
// Now do the actual processing
|
// Now do the actual processing
|
||||||
if (needsResync) {
|
if (needsResync) {
|
||||||
needsResync = false;
|
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() {
|
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)
|
// Invoke change callbacks without the lock! (try must surround sync)
|
||||||
synchronized (plugin.lock) {
|
synchronized (ctx.lock) {
|
||||||
resyncEntries(cc);
|
resyncEntries(cc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -124,7 +124,7 @@ class InfoPerTrace extends TraceDomainObjectListener {
|
|||||||
}
|
}
|
||||||
outboundByRange.remove(me.getTraceAddressSnapRange());
|
outboundByRange.remove(me.getTraceAddressSnapRange());
|
||||||
outboundByStaticUrl.removeMapping(me.getStaticProgramUrl(), me);
|
outboundByStaticUrl.removeMapping(me.getStaticProgramUrl(), me);
|
||||||
plugin.checkAndClearProgram(cc, me);
|
ctx.checkAndClearProgram(cc, me);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processAddedEntries(ChangeCollector cc, Set<TraceStaticMapping> added) {
|
private void processAddedEntries(ChangeCollector cc, Set<TraceStaticMapping> added) {
|
||||||
@@ -138,7 +138,7 @@ class InfoPerTrace extends TraceDomainObjectListener {
|
|||||||
outboundByEntry.put(entry, me);
|
outboundByEntry.put(entry, me);
|
||||||
outboundByRange.put(me.getTraceAddressSnapRange(), me);
|
outboundByRange.put(me.getTraceAddressSnapRange(), me);
|
||||||
outboundByStaticUrl.put(me.getStaticProgramUrl(), me);
|
outboundByStaticUrl.put(me.getStaticProgramUrl(), me);
|
||||||
plugin.checkAndFillProgram(cc, me);
|
ctx.checkAndFillProgram(cc, me);
|
||||||
}
|
}
|
||||||
|
|
||||||
void clearEntriesForProgram(ChangeCollector cc, InfoPerProgram progInfo) {
|
void clearEntriesForProgram(ChangeCollector cc, InfoPerProgram progInfo) {
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@ package ghidra.app.plugin.core.debug.service.modules;
|
|||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.Objects;
|
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.address.*;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.program.util.ProgramLocation;
|
import ghidra.program.util.ProgramLocation;
|
||||||
|
|||||||
+1
-1
@@ -24,8 +24,8 @@ import docking.ActionContext;
|
|||||||
import ghidra.app.context.NavigatableActionContext;
|
import ghidra.app.context.NavigatableActionContext;
|
||||||
import ghidra.app.nav.Navigatable;
|
import ghidra.app.nav.Navigatable;
|
||||||
import ghidra.app.services.*;
|
import ghidra.app.services.*;
|
||||||
import ghidra.app.services.DebuggerStaticMappingService.MappedAddressRange;
|
|
||||||
import ghidra.async.AsyncUtils;
|
import ghidra.async.AsyncUtils;
|
||||||
|
import ghidra.debug.api.modules.MappedAddressRange;
|
||||||
import ghidra.debug.api.target.Target;
|
import ghidra.debug.api.target.Target;
|
||||||
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
|
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
|
|||||||
+3
-3
@@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* 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 java.util.Objects;
|
||||||
|
|
||||||
import ghidra.app.services.DebuggerStaticMappingService;
|
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.address.*;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.program.model.mem.*;
|
import ghidra.program.model.mem.*;
|
||||||
|
|||||||
+1
-1
@@ -27,7 +27,7 @@ import db.Transaction;
|
|||||||
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerTest;
|
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerTest;
|
||||||
import ghidra.app.plugin.core.debug.gui.modules.DebuggerModulesProviderTest;
|
import ghidra.app.plugin.core.debug.gui.modules.DebuggerModulesProviderTest;
|
||||||
import ghidra.app.services.DebuggerStaticMappingService;
|
import ghidra.app.services.DebuggerStaticMappingService;
|
||||||
import ghidra.app.services.DebuggerStaticMappingService.MappedAddressRange;
|
import ghidra.debug.api.modules.MappedAddressRange;
|
||||||
import ghidra.framework.model.DomainFile;
|
import ghidra.framework.model.DomainFile;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
|
|||||||
@@ -29,6 +29,12 @@ public interface AsyncUtils {
|
|||||||
|
|
||||||
ExecutorService FRAMEWORK_EXECUTOR = Executors.newWorkStealingPool();
|
ExecutorService FRAMEWORK_EXECUTOR = Executors.newWorkStealingPool();
|
||||||
ExecutorService SWING_EXECUTOR = SwingExecutorService.LATER;
|
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.
|
// NB. This was a bad idea, because CFs may maintain refs to dependents.
|
||||||
//CompletableFuture<Void> NIL = CompletableFuture.completedFuture(null);
|
//CompletableFuture<Void> NIL = CompletableFuture.completedFuture(null);
|
||||||
|
|||||||
Reference in New Issue
Block a user