mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-21 09:21:35 +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.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
|
||||
*
|
||||
* <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
|
||||
*
|
||||
|
||||
+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.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;
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+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.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<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
|
||||
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<DebuggerStaticMappingChangeListener> 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<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
|
||||
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<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
|
||||
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> 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
|
||||
public Set<Program> openMappedProgramsInView(Trace trace, AddressSetView set, long snap,
|
||||
Set<Exception> failures) {
|
||||
Set<URL> urls;
|
||||
synchronized (lock) {
|
||||
InfoPerTrace info = requireTrackedInfo(trace);
|
||||
if (info == null) {
|
||||
return null;
|
||||
}
|
||||
urls = info.getMappedProgramUrlsInView(set, Lifespan.at(snap));
|
||||
}
|
||||
Set<URL> urls = context.getMappedProgramUrlsInView(trace, set, snap);
|
||||
Set<Program> result = new HashSet<>();
|
||||
for (URL url : urls) {
|
||||
try {
|
||||
@@ -545,6 +278,49 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
|
||||
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(
|
||||
Collection<? extends Program> programs) {
|
||||
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.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;
|
||||
|
||||
+13
-8
@@ -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<Address, MappingEntry> 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);
|
||||
}
|
||||
}
|
||||
|
||||
+10
-10
@@ -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<TraceStaticMapping, MappingEntry> 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<TraceStaticMapping> 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) {
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+3
-3
@@ -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.*;
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
@@ -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<Void> NIL = CompletableFuture.completedFuture(null);
|
||||
|
||||
Reference in New Issue
Block a user