GP-4868: Re-write StaticMappingService. Fix tests.

This commit is contained in:
Dan 2024-08-26 14:28:33 -04:00
parent db28b29dab
commit 78fb4e7077
15 changed files with 1273 additions and 1081 deletions

View file

@ -65,7 +65,9 @@ class TraceBreakpointSet {
@Override @Override
public String toString() { public String toString() {
return String.format("<at %s in %s: %s>", address, trace.getName(), breakpoints); synchronized (breakpoints) {
return String.format("<at %s in %s: %s>", address, trace.getName(), breakpoints);
}
} }
/** /**
@ -126,22 +128,24 @@ class TraceBreakpointSet {
*/ */
public TraceMode computeMode() { public TraceMode computeMode() {
TraceMode mode = TraceMode.NONE; TraceMode mode = TraceMode.NONE;
if (getControlMode().useEmulatedBreakpoints()) { synchronized (breakpoints) {
if (getControlMode().useEmulatedBreakpoints()) {
for (IDHashed<TraceBreakpoint> bpt : breakpoints) {
mode = mode.combine(computeEmuMode(bpt.obj));
if (mode == TraceMode.MISSING) {
return mode;
}
}
return mode;
}
for (IDHashed<TraceBreakpoint> bpt : breakpoints) { for (IDHashed<TraceBreakpoint> bpt : breakpoints) {
mode = mode.combine(computeEmuMode(bpt.obj)); mode = mode.combine(computeTargetMode(bpt.obj));
if (mode == TraceMode.MISSING) { if (mode == TraceMode.MISSING) {
return mode; return mode;
} }
} }
return mode; return mode;
} }
for (IDHashed<TraceBreakpoint> bpt : breakpoints) {
mode = mode.combine(computeTargetMode(bpt.obj));
if (mode == TraceMode.MISSING) {
return mode;
}
}
return mode;
} }
/** /**
@ -188,14 +192,16 @@ class TraceBreakpointSet {
*/ */
public String computeSleigh() { public String computeSleigh() {
String sleigh = null; String sleigh = null;
for (IDHashed<TraceBreakpoint> bpt : breakpoints) { synchronized (breakpoints) {
String s = bpt.obj.getEmuSleigh(); for (IDHashed<TraceBreakpoint> bpt : breakpoints) {
if (sleigh != null && !sleigh.equals(s)) { String s = bpt.obj.getEmuSleigh();
return null; if (sleigh != null && !sleigh.equals(s)) {
return null;
}
sleigh = s;
} }
sleigh = s; return sleigh;
} }
return sleigh;
} }
/** /**
@ -206,8 +212,10 @@ class TraceBreakpointSet {
public void setEmuSleigh(String emuSleigh) { public void setEmuSleigh(String emuSleigh) {
this.emuSleigh = emuSleigh; this.emuSleigh = emuSleigh;
try (Transaction tx = trace.openTransaction("Set breakpoint Sleigh")) { try (Transaction tx = trace.openTransaction("Set breakpoint Sleigh")) {
for (IDHashed<TraceBreakpoint> bpt : breakpoints) { synchronized (breakpoints) {
bpt.obj.setEmuSleigh(emuSleigh); for (IDHashed<TraceBreakpoint> bpt : breakpoints) {
bpt.obj.setEmuSleigh(emuSleigh);
}
} }
} }
} }
@ -218,7 +226,9 @@ class TraceBreakpointSet {
* @return true if empty, false otherwise * @return true if empty, false otherwise
*/ */
public boolean isEmpty() { public boolean isEmpty() {
return breakpoints.isEmpty(); synchronized (breakpoints) {
return breakpoints.isEmpty();
}
} }
/** /**
@ -227,7 +237,9 @@ class TraceBreakpointSet {
* @return the breakpoints * @return the breakpoints
*/ */
public Set<TraceBreakpoint> getBreakpoints() { public Set<TraceBreakpoint> getBreakpoints() {
return breakpoints.stream().map(e -> e.obj).collect(Collectors.toUnmodifiableSet()); synchronized (breakpoints) {
return breakpoints.stream().map(e -> e.obj).collect(Collectors.toUnmodifiableSet());
}
} }
/** /**
@ -246,7 +258,9 @@ class TraceBreakpointSet {
bpt.setEmuSleigh(emuSleigh); bpt.setEmuSleigh(emuSleigh);
} }
} }
return breakpoints.add(new IDHashed<>(bpt)); synchronized (breakpoints) {
return breakpoints.add(new IDHashed<>(bpt));
}
} }
/** /**
@ -275,7 +289,9 @@ class TraceBreakpointSet {
* @return true if the set actually changes as a result * @return true if the set actually changes as a result
*/ */
public boolean remove(TraceBreakpoint bpt) { public boolean remove(TraceBreakpoint bpt) {
return breakpoints.remove(new IDHashed<>(bpt)); synchronized (breakpoints) {
return breakpoints.remove(new IDHashed<>(bpt));
}
} }
/** /**
@ -303,7 +319,7 @@ class TraceBreakpointSet {
public void planEnable(BreakpointActionSet actions, long length, public void planEnable(BreakpointActionSet actions, long length,
Collection<TraceBreakpointKind> kinds) { Collection<TraceBreakpointKind> kinds) {
long snap = getSnap(); long snap = getSnap();
if (breakpoints.isEmpty()) { if (isEmpty()) {
if (target == null || getControlMode().useEmulatedBreakpoints()) { if (target == null || getControlMode().useEmulatedBreakpoints()) {
planPlaceEmu(actions, snap, length, kinds); planPlaceEmu(actions, snap, length, kinds);
} }
@ -339,14 +355,18 @@ class TraceBreakpointSet {
} }
private void planEnableTarget(BreakpointActionSet actions) { private void planEnableTarget(BreakpointActionSet actions) {
for (IDHashed<TraceBreakpoint> bpt : breakpoints) { synchronized (breakpoints) {
actions.planEnableTarget(target, bpt.obj); for (IDHashed<TraceBreakpoint> bpt : breakpoints) {
actions.planEnableTarget(target, bpt.obj);
}
} }
} }
private void planEnableEmu(BreakpointActionSet actions) { private void planEnableEmu(BreakpointActionSet actions) {
for (IDHashed<TraceBreakpoint> bpt : breakpoints) { synchronized (breakpoints) {
actions.planEnableEmu(bpt.obj); for (IDHashed<TraceBreakpoint> bpt : breakpoints) {
actions.planEnableEmu(bpt.obj);
}
} }
} }
@ -369,14 +389,18 @@ class TraceBreakpointSet {
private void planDisableTarget(BreakpointActionSet actions, long length, private void planDisableTarget(BreakpointActionSet actions, long length,
Collection<TraceBreakpointKind> kinds) { Collection<TraceBreakpointKind> kinds) {
for (IDHashed<TraceBreakpoint> bpt : breakpoints) { synchronized (breakpoints) {
actions.planDisableTarget(target, bpt.obj); for (IDHashed<TraceBreakpoint> bpt : breakpoints) {
actions.planDisableTarget(target, bpt.obj);
}
} }
} }
private void planDisableEmu(BreakpointActionSet actions) { private void planDisableEmu(BreakpointActionSet actions) {
for (IDHashed<TraceBreakpoint> bpt : breakpoints) { synchronized (breakpoints) {
actions.planDisableEmu(bpt.obj); for (IDHashed<TraceBreakpoint> bpt : breakpoints) {
actions.planDisableEmu(bpt.obj);
}
} }
} }
@ -399,14 +423,18 @@ class TraceBreakpointSet {
private void planDeleteTarget(BreakpointActionSet actions, long length, private void planDeleteTarget(BreakpointActionSet actions, long length,
Set<TraceBreakpointKind> kinds) { Set<TraceBreakpointKind> kinds) {
for (IDHashed<TraceBreakpoint> bpt : breakpoints) { synchronized (breakpoints) {
actions.planDeleteTarget(target, bpt.obj); for (IDHashed<TraceBreakpoint> bpt : breakpoints) {
actions.planDeleteTarget(target, bpt.obj);
}
} }
} }
private void planDeleteEmu(BreakpointActionSet actions) { private void planDeleteEmu(BreakpointActionSet actions) {
for (IDHashed<TraceBreakpoint> bpt : breakpoints) { synchronized (breakpoints) {
actions.planDeleteEmu(bpt.obj); for (IDHashed<TraceBreakpoint> bpt : breakpoints) {
actions.planDeleteEmu(bpt.obj);
}
} }
} }
} }

View file

@ -0,0 +1,180 @@
/* ###
* 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.CompletableFuture;
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingServicePlugin.ChangeCollector;
import ghidra.app.plugin.core.debug.utils.ProgramURLUtils;
import ghidra.app.services.DebuggerStaticMappingService.MappedAddressRange;
import ghidra.framework.model.*;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.trace.model.*;
import ghidra.util.Msg;
class InfoPerProgram implements DomainObjectListener {
static class NavMultiMap<K, V> {
private final TreeMap<K, Set<V>> map = new TreeMap<>();
public boolean put(K k, V v) {
return map.computeIfAbsent(k, __ -> new HashSet<>()).add(v);
}
public boolean remove(K k, V v) {
Set<V> set = map.get(k);
if (set == null) {
return false;
}
if (!set.remove(v)) {
return false;
}
if (set.isEmpty()) {
map.remove(k);
}
return true;
}
}
private final DebuggerStaticMappingServicePlugin plugin;
final Program program;
final NavMultiMap<Address, MappingEntry> inboundByStaticAddress = new NavMultiMap<>();
final URL url;
InfoPerProgram(DebuggerStaticMappingServicePlugin plugin, Program program) {
this.plugin = plugin;
this.program = program;
this.url = ProgramURLUtils.getUrlFromProgram(program);
program.addListener(this);
}
@Override
public void domainObjectChanged(DomainObjectChangedEvent ev) {
if (ev.contains(DomainObjectEvent.FILE_CHANGED) || ev.contains(DomainObjectEvent.RENAMED)) {
if (!urlMatches()) {
CompletableFuture.runAsync(plugin::programsChanged, plugin.executor);
}
}
}
boolean urlMatches() {
return Objects.equals(url, ProgramURLUtils.getUrlFromProgram(program));
}
void clearProgram(ChangeCollector cc, MappingEntry me) {
assert me.program == program;
inboundByStaticAddress.remove(me.getStaticAddress(), me);
me.clearProgram(cc, program);
}
void fillProgram(ChangeCollector cc, MappingEntry me) {
assert me.getStaticProgramUrl().equals(ProgramURLUtils.getUrlFromProgram(program));
me.fillProgram(cc, program);
inboundByStaticAddress.put(me.getStaticAddress(), me);
}
void clearEntries(ChangeCollector cc) {
if (url == null) {
return;
}
for (InfoPerTrace info : plugin.traceInfoByTrace.values()) {
info.clearEntriesForProgram(cc, this);
}
}
void fillEntries(ChangeCollector cc) {
if (url == null) {
return;
}
for (InfoPerTrace info : plugin.traceInfoByTrace.values()) {
info.fillEntriesForProgram(cc, this);
}
}
Set<TraceLocation> getOpenMappedTraceLocations(Address address) {
Set<TraceLocation> result = new HashSet<>();
for (Set<MappingEntry> set : inboundByStaticAddress.map.headMap(address, true).values()) {
for (MappingEntry me : set) {
if (me.mapping.isDeleted()) {
Msg.warn(this, "Encountered deleted mapping: " + me.mapping);
continue;
}
if (!me.isInProgramRange(address)) {
continue;
}
result.add(me.mapProgramAddressToTraceLocation(address));
}
}
return result;
}
TraceLocation getOpenMappedTraceLocation(Trace trace, Address address, long snap) {
// TODO: Map by trace?
for (Set<MappingEntry> set : inboundByStaticAddress.map.headMap(address, true).values()) {
for (MappingEntry me : set) {
if (me.mapping.isDeleted()) {
Msg.warn(this, "Encountered deleted mapping: " + me.mapping);
continue;
}
if (me.getTrace() != trace) {
continue;
}
if (!me.isInProgramRange(address)) {
continue;
}
if (!me.isInTraceLifespan(snap)) {
continue;
}
return me.mapProgramAddressToTraceLocation(address);
}
}
return null;
}
private void collectOpenMappedViews(Map<TraceSpan, Collection<MappedAddressRange>> result,
AddressRange rng) {
for (Set<MappingEntry> set : inboundByStaticAddress.map.headMap(rng.getMaxAddress(), true)
.values()) {
for (MappingEntry me : set) {
if (me.mapping.isDeleted()) {
Msg.warn(this, "Encountered deleted mapping: " + me.mapping);
continue;
}
// NB. No lifespan to consider
if (!me.isInProgramRange(rng)) {
continue;
}
AddressRange srcRange = me.getStaticRange().intersect(rng);
AddressRange dstRange = me.mapProgramRangeToTrace(rng);
result.computeIfAbsent(me.getTraceSpan(), p -> new TreeSet<>())
.add(new MappedAddressRange(srcRange, dstRange));
}
}
}
Map<TraceSpan, Collection<MappedAddressRange>> getOpenMappedViews(AddressSetView set) {
Map<TraceSpan, Collection<MappedAddressRange>> result = new HashMap<>();
for (AddressRange rng : set) {
collectOpenMappedViews(result, rng);
}
return Collections.unmodifiableMap(result);
}
}

View file

@ -0,0 +1,254 @@
/* ###
* 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.Map.Entry;
import java.util.concurrent.CompletableFuture;
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.framework.model.DomainObjectChangedEvent;
import ghidra.framework.model.DomainObjectEvent;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.program.util.ProgramLocation;
import ghidra.trace.model.*;
import ghidra.trace.model.modules.TraceStaticMapping;
import ghidra.trace.util.TraceEvents;
import ghidra.util.Msg;
class InfoPerTrace extends TraceDomainObjectListener {
private final DebuggerStaticMappingServicePlugin plugin;
final Trace trace;
final Map<TraceStaticMapping, MappingEntry> outboundByEntry = new HashMap<>();
final NavigableMap<TraceAddressSnapRange, MappingEntry> outboundByRange =
new TreeMap<>(Comparator.comparing(TraceAddressSnapRange::getX1));
final MultiValuedMap<URL, MappingEntry> outboundByStaticUrl = new HashSetValuedHashMap<>();
private volatile boolean needsResync = false;
InfoPerTrace(DebuggerStaticMappingServicePlugin plugin, Trace trace) {
this.plugin = plugin;
this.trace = trace;
listenForUntyped(DomainObjectEvent.RESTORED, e -> objectRestored());
listenFor(TraceEvents.MAPPING_ADDED, this::staticMappingAdded);
listenFor(TraceEvents.MAPPING_DELETED, this::staticMappingDeleted);
trace.addListener(this);
}
@Override
public void domainObjectChanged(DomainObjectChangedEvent ev) {
super.domainObjectChanged(ev); // Dispatch individual records
// Now do the actual processing
if (needsResync) {
needsResync = false;
CompletableFuture.runAsync(this::resyncEntries, plugin.executor);
}
}
private void objectRestored() {
this.needsResync = true;
}
private void staticMappingAdded(TraceStaticMapping mapping) {
this.needsResync = true;
}
private void staticMappingDeleted(TraceStaticMapping mapping) {
this.needsResync = true;
}
public void dispose() {
trace.removeListener(this);
}
private void resyncEntries() {
try (ChangeCollector cc = new ChangeCollector(plugin)) {
// Invoke change callbacks without the lock! (try must surround sync)
synchronized (plugin.lock) {
resyncEntries(cc);
}
}
}
void resyncEntries(ChangeCollector cc) {
Set<TraceStaticMapping> oldEntries = outboundByEntry.keySet();
Set<TraceStaticMapping> curEntries = trace.getStaticMappingManager()
.getAllEntries()
.stream()
.filter(e -> !e.isDeleted()) // Double-check
.collect(Collectors.toSet());
Set<TraceStaticMapping> removed = ChangeCollector.subtract(oldEntries, curEntries);
Set<TraceStaticMapping> added = ChangeCollector.subtract(curEntries, oldEntries);
processRemovedEntries(cc, removed);
processAddedEntries(cc, added);
}
void removeEntries(ChangeCollector cc) {
processRemovedEntries(cc, Set.copyOf(outboundByEntry.keySet()));
}
private void processRemovedEntries(ChangeCollector cc, Set<TraceStaticMapping> removed) {
for (TraceStaticMapping entry : removed) {
processRemovedEntry(cc, entry);
}
}
private void processRemovedEntry(ChangeCollector cc, TraceStaticMapping entry) {
MappingEntry me = outboundByEntry.remove(entry);
if (me == null) {
return;
}
outboundByRange.remove(me.getTraceAddressSnapRange());
outboundByStaticUrl.removeMapping(me.getStaticProgramUrl(), me);
plugin.checkAndClearProgram(cc, me);
}
private void processAddedEntries(ChangeCollector cc, Set<TraceStaticMapping> added) {
for (TraceStaticMapping entry : added) {
processAddedEntry(cc, entry);
}
}
private void processAddedEntry(ChangeCollector cc, TraceStaticMapping entry) {
MappingEntry me = new MappingEntry(entry);
outboundByEntry.put(entry, me);
outboundByRange.put(me.getTraceAddressSnapRange(), me);
outboundByStaticUrl.put(me.getStaticProgramUrl(), me);
plugin.checkAndFillProgram(cc, me);
}
void clearEntriesForProgram(ChangeCollector cc, InfoPerProgram progInfo) {
for (MappingEntry me : outboundByStaticUrl.get(progInfo.url)) {
progInfo.clearProgram(cc, me);
}
}
void fillEntriesForProgram(ChangeCollector cc, InfoPerProgram progInfo) {
for (MappingEntry me : outboundByStaticUrl.get(progInfo.url)) {
progInfo.fillProgram(cc, me);
}
}
Set<Program> getOpenMappedProgramsAtSnap(long snap) {
Set<Program> result = new HashSet<>();
for (Entry<TraceAddressSnapRange, MappingEntry> out : outboundByRange.entrySet()) {
MappingEntry me = out.getValue();
if (me.mapping.isDeleted()) {
Msg.warn(this, "Encountered deleted mapping: " + me.mapping);
continue;
}
if (!me.isStaticProgramOpen()) {
continue;
}
if (!out.getKey().getLifespan().contains(snap)) {
continue;
}
result.add(me.program);
}
return result;
}
ProgramLocation getOpenMappedProgramLocation(Address address, Lifespan span) {
TraceAddressSnapRange tasr = new ImmutableTraceAddressSnapRange(address, span);
// max is tasr (single address)
for (MappingEntry me : outboundByRange.headMap(tasr, true).values()) {
if (me.mapping.isDeleted()) {
Msg.warn(this, "Encountered deleted mapping: " + me.mapping);
continue;
}
if (!tasr.intersects(me.getTraceAddressSnapRange())) {
continue;
}
if (me.isStaticProgramOpen()) {
return me.mapTraceAddressToProgramLocation(address);
}
}
return null;
}
private void collectOpenMappedViews(Map<Program, Collection<MappedAddressRange>> result,
AddressRange rng, Lifespan span) {
TraceAddressSnapRange tasr = new ImmutableTraceAddressSnapRange(rng, span);
TraceAddressSnapRange max = new ImmutableTraceAddressSnapRange(rng.getMaxAddress(), span);
for (MappingEntry me : outboundByRange.headMap(max, true).values()) {
if (me.mapping.isDeleted()) {
Msg.warn(this, "Encountered deleted mapping: " + me.mapping);
continue;
}
if (me.program == null) {
continue;
}
if (!tasr.intersects(me.getTraceAddressSnapRange())) {
continue;
}
AddressRange srcRng = me.getTraceRange().intersect(rng);
AddressRange dstRng = me.mapTraceRangeToProgram(rng);
result.computeIfAbsent(me.program, p -> new TreeSet<>())
.add(new MappedAddressRange(srcRng, dstRng));
}
}
Map<Program, Collection<MappedAddressRange>> getOpenMappedViews(AddressSetView set,
Lifespan span) {
/**
* NB. Cannot use the OverlappingObjectIterator here. Because of the snap dimension, objects
* may not be disjoint in the address dimension.
*/
Map<Program, Collection<MappedAddressRange>> result = new HashMap<>();
for (AddressRange rng : set) {
collectOpenMappedViews(result, rng, span);
}
return Collections.unmodifiableMap(result);
}
private void collectMappedProgramUrlsInView(Set<URL> result, AddressRange rng, Lifespan span) {
TraceAddressSnapRange tasr = new ImmutableTraceAddressSnapRange(rng, span);
TraceAddressSnapRange max = new ImmutableTraceAddressSnapRange(rng.getMaxAddress(), span);
for (MappingEntry me : outboundByRange.headMap(max, true).values()) {
if (me.mapping.isDeleted()) {
Msg.warn(this, "Encountered deleted mapping: " + me.mapping);
continue;
}
if (!tasr.intersects(me.getTraceAddressSnapRange())) {
continue;
}
result.add(me.getStaticProgramUrl());
}
}
Set<URL> getMappedProgramUrlsInView(AddressSetView set, Lifespan span) {
/**
* NB. Cannot use the OverlappingObjectIterator here. Because of the snap dimension, objects
* may not be disjoint in the address dimension.
*/
Set<URL> result = new HashSet<>();
for (AddressRange rng : set) {
collectMappedProgramUrlsInView(result, rng, span);
}
return Collections.unmodifiableSet(result);
}
}

View file

@ -0,0 +1,202 @@
/* ###
* 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.Objects;
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingServicePlugin.ChangeCollector;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.program.util.ProgramLocation;
import ghidra.trace.model.*;
import ghidra.trace.model.modules.TraceStaticMapping;
import ghidra.util.Msg;
class MappingEntry {
final TraceStaticMapping mapping;
final TraceAddressSnapRange tasr;
Program program;
private AddressRange staticRange;
public MappingEntry(TraceStaticMapping mapping) {
this.mapping = mapping;
// Yes, mapping range and lifespan are immutable
this.tasr = new ImmutableTraceAddressSnapRange(mapping.getTraceAddressRange(),
mapping.getLifespan());
}
@Override
public boolean equals(Object o) {
if (!(o instanceof MappingEntry that)) {
return false;
}
// Yes, use identity, since it should be the same trace db records
if (this.mapping != that.mapping) {
return false;
}
if (this.program != that.program) {
return false;
}
if (!Objects.equals(this.staticRange, that.staticRange)) {
return false;
}
return true;
}
public Trace getTrace() {
return mapping.getTrace();
}
Address addrOrMin(Program program, String addr) {
AddressFactory factory = program.getAddressFactory();
Address result = factory.getAddress(addr);
if (result == null) {
Msg.warn(this, "Mapping entry has invalid static address: " + addr);
result = factory.getDefaultAddressSpace().getMinAddress();
}
return result;
}
Address addrOrMax(Address start, long length) {
Address result = start.addWrapSpace(length);
if (result.compareTo(start) < 0) {
Msg.warn(this, "Mapping entry caused overflow in static address space");
return start.getAddressSpace().getMaxAddress();
}
return result;
}
void clearProgram(ChangeCollector cc, Program program) {
this.program = null;
this.staticRange = null;
cc.traceAffected(getTrace());
cc.programAffected(program);
}
void fillProgram(ChangeCollector cc, Program program) {
this.program = program;
Address minAddr = addrOrMin(program, mapping.getStaticAddress());
Address maxAddr = addrOrMax(minAddr, mapping.getLength() - 1);
this.staticRange = new AddressRangeImpl(minAddr, maxAddr);
cc.traceAffected(getTrace());
cc.programAffected(program);
}
public AddressRange getTraceRange() {
return mapping.getTraceAddressRange();
}
public Address getTraceAddress() {
return mapping.getMinTraceAddress();
}
public AddressRange getStaticRange() {
return staticRange;
}
public Address getStaticAddress() {
if (staticRange == null) {
return null;
}
return staticRange.getMinAddress();
}
public TraceSpan getTraceSpan() {
return new DefaultTraceSpan(mapping.getTrace(), mapping.getLifespan());
}
public TraceAddressSnapRange getTraceAddressSnapRange() {
return tasr;
}
public boolean isInTraceRange(Address address, Long snap) {
return mapping.getTraceAddressRange().contains(address) &&
(snap == null || mapping.getLifespan().contains(snap));
}
public boolean isInTraceRange(AddressRange rng, Long snap) {
return mapping.getTraceAddressRange().intersects(rng) &&
(snap == null || mapping.getLifespan().contains(snap));
}
public boolean isInTraceLifespan(long snap) {
return mapping.getLifespan().contains(snap);
}
public boolean isInProgramRange(Address address) {
if (staticRange == null) {
return false;
}
return staticRange.contains(address);
}
public boolean isInProgramRange(AddressRange rng) {
if (staticRange == null) {
return false;
}
return staticRange.intersects(rng);
}
protected Address mapTraceAddressToProgram(Address address) {
assert isInTraceRange(address, null);
long offset = address.subtract(mapping.getMinTraceAddress());
return staticRange.getMinAddress().addWrapSpace(offset);
}
public ProgramLocation mapTraceAddressToProgramLocation(Address address) {
if (program == null) {
throw new IllegalStateException("Static program is not opened");
}
return new ProgramLocation(program, mapTraceAddressToProgram(address));
}
public AddressRange mapTraceRangeToProgram(AddressRange rng) {
assert isInTraceRange(rng, null);
AddressRange part = rng.intersect(mapping.getTraceAddressRange());
Address min = mapTraceAddressToProgram(part.getMinAddress());
Address max = mapTraceAddressToProgram(part.getMaxAddress());
return new AddressRangeImpl(min, max);
}
protected Address mapProgramAddressToTrace(Address address) {
assert isInProgramRange(address);
long offset = address.subtract(staticRange.getMinAddress());
return mapping.getMinTraceAddress().addWrapSpace(offset);
}
protected TraceLocation mapProgramAddressToTraceLocation(Address address) {
return new DefaultTraceLocation(mapping.getTrace(), null, mapping.getLifespan(),
mapProgramAddressToTrace(address));
}
public AddressRange mapProgramRangeToTrace(AddressRange rng) {
assert (rng.intersects(staticRange));
AddressRange part = rng.intersect(staticRange);
Address min = mapProgramAddressToTrace(part.getMinAddress());
Address max = mapProgramAddressToTrace(part.getMaxAddress());
return new AddressRangeImpl(min, max);
}
public boolean isStaticProgramOpen() {
return program != null;
}
public URL getStaticProgramUrl() {
return mapping.getStaticProgramURL();
}
}

View file

@ -15,7 +15,7 @@
*/ */
package ghidra.app.plugin.core.debug.service.tracemgr; package ghidra.app.plugin.core.debug.service.tracemgr;
import static ghidra.framework.main.DataTreeDialogType.*; import static ghidra.framework.main.DataTreeDialogType.OPEN;
import java.io.IOException; import java.io.IOException;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
@ -656,7 +656,9 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
@Override @Override
public synchronized Collection<Trace> getOpenTraces() { public synchronized Collection<Trace> getOpenTraces() {
return Set.copyOf(tracesView); synchronized (listenersByTrace) {
return Set.copyOf(tracesView);
}
} }
@Override @Override
@ -998,8 +1000,8 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
protected void doCloseTraces(Collection<Trace> traces, Collection<Target> targets) { protected void doCloseTraces(Collection<Trace> traces, Collection<Target> targets) {
for (Trace t : traces) { for (Trace t : traces) {
if (t.getConsumerList().contains(this)) { if (t.getConsumerList().contains(this)) {
firePluginEvent(new TraceClosedPluginEvent(getName(), t));
doTraceClosed(t); doTraceClosed(t);
firePluginEvent(new TraceClosedPluginEvent(getName(), t));
} }
} }
TargetActionTask.executeTask(tool, new DisconnectTask(tool, targets)); TargetActionTask.executeTask(tool, new DisconnectTask(tool, targets));

View file

@ -614,8 +614,7 @@ public abstract class AbstractGhidraHeadedDebuggerTest
@BeforeClass @BeforeClass
public static void beforeClass() { public static void beforeClass() {
// Note: we decided to move this up to a framework-level base test class
// Note: we may decided to move this up to a framework-level base test class
TestDataStructureErrorHandlerInstaller.installConcurrentExceptionErrorHandler(); TestDataStructureErrorHandlerInstaller.installConcurrentExceptionErrorHandler();
} }

View file

@ -95,6 +95,7 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
try (ToyDBTraceBuilder r = new ToyDBTraceBuilder(saved)) { try (ToyDBTraceBuilder r = new ToyDBTraceBuilder(saved)) {
assertNotSame(tb.trace, r.trace); assertNotSame(tb.trace, r.trace);
traceManager.openTrace(r.trace); traceManager.openTrace(r.trace);
waitForDomainObject(r.trace);
return r.trace; return r.trace;
} }
} }
@ -240,10 +241,11 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
} }
@Test @Test
public void testAddMappingThenCopyAndTranslateStaticToTraceMissWayBefore() throws Exception { public void testAddMappingThenCopyAndTranslateStaticToTraceMissWayBefore() throws Throwable {
addMapping(); addMapping();
copyTrace(); copyTrace();
add2ndMapping(); add2ndMapping();
waitOn(mappingService.changesSettled());
Set<TraceLocation> locations = mappingService.getOpenMappedLocations( Set<TraceLocation> locations = mappingService.getOpenMappedLocations(
new ProgramLocation(program, stSpace.getAddress(0x00000bad))); new ProgramLocation(program, stSpace.getAddress(0x00000bad)));
@ -251,10 +253,11 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
} }
@Test @Test
public void testAddMappingThenCopyAndTranslateStaticToTraceMissJustBefore() throws Exception { public void testAddMappingThenCopyAndTranslateStaticToTraceMissJustBefore() throws Throwable {
addMapping(); addMapping();
copyTrace(); copyTrace();
add2ndMapping(); add2ndMapping();
waitOn(mappingService.changesSettled());
Set<TraceLocation> locations = mappingService.getOpenMappedLocations( Set<TraceLocation> locations = mappingService.getOpenMappedLocations(
new ProgramLocation(program, stSpace.getAddress(0x001fffff))); new ProgramLocation(program, stSpace.getAddress(0x001fffff)));
@ -262,10 +265,11 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
} }
@Test @Test
public void testAddMappingThenCopyAndTranslateStaticToTraceHitAtStart() throws Exception { public void testAddMappingThenCopyAndTranslateStaticToTraceHitAtStart() throws Throwable {
addMapping(); addMapping();
Trace copy = copyTrace(); Trace copy = copyTrace();
add2ndMapping(); add2ndMapping();
waitOn(mappingService.changesSettled());
Set<TraceLocation> locations = mappingService.getOpenMappedLocations( Set<TraceLocation> locations = mappingService.getOpenMappedLocations(
new ProgramLocation(program, stSpace.getAddress(0x00200000))); new ProgramLocation(program, stSpace.getAddress(0x00200000)));
@ -281,10 +285,11 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
} }
@Test @Test
public void testAddMappingThenCopyAndTranslateStaticToTraceHitInMiddle() throws Exception { public void testAddMappingThenCopyAndTranslateStaticToTraceHitInMiddle() throws Throwable {
addMapping(); addMapping();
Trace copy = copyTrace(); Trace copy = copyTrace();
add2ndMapping(); add2ndMapping();
waitOn(mappingService.changesSettled());
Set<TraceLocation> locations = mappingService.getOpenMappedLocations( Set<TraceLocation> locations = mappingService.getOpenMappedLocations(
new ProgramLocation(program, stSpace.getAddress(0x00200833))); new ProgramLocation(program, stSpace.getAddress(0x00200833)));
@ -298,10 +303,11 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
} }
@Test @Test
public void testAddMappingThenCopyAndTranslateStaticToTraceHitAtEnd() throws Exception { public void testAddMappingThenCopyAndTranslateStaticToTraceHitAtEnd() throws Throwable {
addMapping(); addMapping();
Trace copy = copyTrace(); Trace copy = copyTrace();
add2ndMapping(); add2ndMapping();
waitOn(mappingService.changesSettled());
Set<TraceLocation> locations = mappingService.getOpenMappedLocations( Set<TraceLocation> locations = mappingService.getOpenMappedLocations(
new ProgramLocation(program, stSpace.getAddress(0x00200fff))); new ProgramLocation(program, stSpace.getAddress(0x00200fff)));
@ -315,10 +321,11 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
} }
@Test @Test
public void testAddMappingThenCopyAndTranslateStaticToTraceMissJustAfter() throws Exception { public void testAddMappingThenCopyAndTranslateStaticToTraceMissJustAfter() throws Throwable {
addMapping(); addMapping();
copyTrace(); copyTrace();
add2ndMapping(); add2ndMapping();
waitOn(mappingService.changesSettled());
Set<TraceLocation> locations = mappingService.getOpenMappedLocations( Set<TraceLocation> locations = mappingService.getOpenMappedLocations(
new ProgramLocation(program, stSpace.getAddress(0x00201000))); new ProgramLocation(program, stSpace.getAddress(0x00201000)));
@ -326,10 +333,11 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
} }
@Test @Test
public void testAddMappingThenCopyAndTranslateStaticToTraceMissWayAfter() throws Exception { public void testAddMappingThenCopyAndTranslateStaticToTraceMissWayAfter() throws Throwable {
addMapping(); addMapping();
copyTrace(); copyTrace();
add2ndMapping(); add2ndMapping();
waitOn(mappingService.changesSettled());
Set<TraceLocation> locations = mappingService.getOpenMappedLocations( Set<TraceLocation> locations = mappingService.getOpenMappedLocations(
new ProgramLocation(program, stSpace.getAddress(0xbadbadbadL))); new ProgramLocation(program, stSpace.getAddress(0xbadbadbadL)));
@ -377,10 +385,11 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
} }
@Test @Test
public void testAddMappingThenTranslateStaticViewToTraceEmpty() throws Exception { public void testAddMappingThenTranslateStaticViewToTraceEmpty() throws Throwable {
addMapping(); addMapping();
copyTrace(); copyTrace();
add2ndMapping(); add2ndMapping();
waitOn(mappingService.changesSettled());
Map<TraceSpan, Collection<MappedAddressRange>> views = Map<TraceSpan, Collection<MappedAddressRange>> views =
mappingService.getOpenMappedViews(program, new AddressSet()); mappingService.getOpenMappedViews(program, new AddressSet());
@ -388,10 +397,11 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
} }
@Test @Test
public void testAddMappingThenTranslateStaticViewToTraceReplete() throws Exception { public void testAddMappingThenTranslateStaticViewToTraceReplete() throws Throwable {
addMapping(); addMapping();
Trace copy = copyTrace(); Trace copy = copyTrace();
add2ndMapping(); add2ndMapping();
waitOn(mappingService.changesSettled());
AddressSet set = new AddressSet(); AddressSet set = new AddressSet();
// Before // Before
@ -438,10 +448,12 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
} }
@Test @Test
public void testAddMappingThenCloseStaticAndOpenMappedMissWayBefore() throws Exception { public void testAddMappingThenCloseStaticAndOpenMappedMissWayBefore() throws Throwable {
// NOTE: Does not make sense to test program->trace, as program has no mapping records // NOTE: Does not make sense to test program->trace, as program has no mapping records
addMapping(); addMapping();
programManager.closeProgram(program, true); programManager.closeProgram(program, true);
waitForSwing();
waitOn(mappingService.changesSettled());
AddressSet addrSet = new AddressSet(dynSpace.getAddress(0x00000bad)); AddressSet addrSet = new AddressSet(dynSpace.getAddress(0x00000bad));
Set<Program> programSet = Set<Program> programSet =
@ -451,9 +463,11 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
} }
@Test @Test
public void testAddMappingThenCloseStaticAndOpenMappedHitInMiddle() throws Exception { public void testAddMappingThenCloseStaticAndOpenMappedHitInMiddle() throws Throwable {
addMapping(); addMapping();
programManager.closeProgram(program, true); programManager.closeProgram(program, true);
waitForSwing();
waitOn(mappingService.changesSettled());
AddressSet addrSet = new AddressSet(dynSpace.getAddress(0x00100c0d)); AddressSet addrSet = new AddressSet(dynSpace.getAddress(0x00100c0d));
Set<Program> programSet = Set<Program> programSet =
@ -465,9 +479,11 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
} }
@Test @Test
public void testAddMappingThenCloseStaticAndOpenMappedMissWayAfter() throws Exception { public void testAddMappingThenCloseStaticAndOpenMappedMissWayAfter() throws Throwable {
addMapping(); addMapping();
programManager.closeProgram(program, true); programManager.closeProgram(program, true);
waitForSwing();
waitOn(mappingService.changesSettled());
AddressSet addrSet = new AddressSet(dynSpace.getAddress(0xbadbadbadL)); AddressSet addrSet = new AddressSet(dynSpace.getAddress(0xbadbadbadL));
Set<Program> programSet = Set<Program> programSet =
@ -478,14 +494,17 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
@Test @Test
public void testAddMappingThenCloseStaticAndTranslateTraceToStaticHitInMiddle() public void testAddMappingThenCloseStaticAndTranslateTraceToStaticHitInMiddle()
throws Exception { throws Throwable {
addMapping(); addMapping();
waitOn(mappingService.changesSettled());
// pre-check // pre-check
assertNotNull(mappingService.getOpenMappedLocation( assertNotNull(mappingService.getOpenMappedLocation(
new DefaultTraceLocation(tb.trace, null, Lifespan.nowOn(0), new DefaultTraceLocation(tb.trace, null, Lifespan.nowOn(0),
dynSpace.getAddress(0x00100c0d)))); dynSpace.getAddress(0x00100c0d))));
programManager.closeProgram(program, true); programManager.closeProgram(program, true);
waitForSwing();
waitOn(mappingService.changesSettled());
assertNull(mappingService.getOpenMappedLocation( assertNull(mappingService.getOpenMappedLocation(
new DefaultTraceLocation(tb.trace, null, Lifespan.nowOn(0), new DefaultTraceLocation(tb.trace, null, Lifespan.nowOn(0),
@ -494,14 +513,16 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
@Test @Test
public void testAddMappingThenCloseTraceAndTranslateStaticToTraceHitInMiddle() public void testAddMappingThenCloseTraceAndTranslateStaticToTraceHitInMiddle()
throws Exception { throws Throwable {
addMapping(); addMapping();
waitOn(mappingService.changesSettled());
// pre-check // pre-check
assertEquals(1, mappingService.getOpenMappedLocations( assertEquals(1, mappingService.getOpenMappedLocations(
new ProgramLocation(program, stSpace.getAddress(0x00200c0d))).size()); new ProgramLocation(program, stSpace.getAddress(0x00200c0d))).size());
traceManager.closeTrace(tb.trace); traceManager.closeTrace(tb.trace);
waitForSwing(); waitForSwing();
waitOn(mappingService.changesSettled());
assertTrue(mappingService.getOpenMappedLocations( assertTrue(mappingService.getOpenMappedLocations(
new ProgramLocation(program, stSpace.getAddress(0x00200c0d))).isEmpty()); new ProgramLocation(program, stSpace.getAddress(0x00200c0d))).isEmpty());
@ -509,9 +530,11 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
@Test @Test
public void testAddMappingThenCloseAndReopenStaticAndTranslateTraceToStaticHitInMiddle() public void testAddMappingThenCloseAndReopenStaticAndTranslateTraceToStaticHitInMiddle()
throws Exception { throws Throwable {
addMapping(); addMapping();
programManager.closeProgram(program, true); programManager.closeProgram(program, true);
waitForSwing();
waitOn(mappingService.changesSettled());
// pre-check // pre-check
assertNull(mappingService.getOpenMappedLocation( assertNull(mappingService.getOpenMappedLocation(
new DefaultTraceLocation(tb.trace, null, Lifespan.nowOn(0), new DefaultTraceLocation(tb.trace, null, Lifespan.nowOn(0),
@ -519,6 +542,7 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
programManager.openProgram(program); programManager.openProgram(program);
waitForProgram(program); waitForProgram(program);
waitOn(mappingService.changesSettled());
assertNotNull(mappingService.getOpenMappedLocation( assertNotNull(mappingService.getOpenMappedLocation(
new DefaultTraceLocation(tb.trace, null, Lifespan.nowOn(0), new DefaultTraceLocation(tb.trace, null, Lifespan.nowOn(0),
@ -527,17 +551,19 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
@Test @Test
public void testAddMappingThenCloseAndReopenTraceAndTranslateStaticToTraceHitInMiddle() public void testAddMappingThenCloseAndReopenTraceAndTranslateStaticToTraceHitInMiddle()
throws Exception { throws Throwable {
addMapping(); addMapping();
traceManager.closeTrace(tb.trace); traceManager.closeTrace(tb.trace);
waitForSwing(); waitForSwing();
waitOn(mappingService.changesSettled());
// pre-check // pre-check
assertTrue(mappingService.getOpenMappedLocations( assertTrue(mappingService.getOpenMappedLocations(
new ProgramLocation(program, stSpace.getAddress(0x00200c0d))).isEmpty()); new ProgramLocation(program, stSpace.getAddress(0x00200c0d))).isEmpty());
traceManager.openTrace(tb.trace); traceManager.openTrace(tb.trace);
waitForSwing(); waitForDomainObject(tb.trace);
waitOn(mappingService.changesSettled());
assertEquals(1, mappingService.getOpenMappedLocations( assertEquals(1, mappingService.getOpenMappedLocations(
new ProgramLocation(program, stSpace.getAddress(0x00200c0d))).size()); new ProgramLocation(program, stSpace.getAddress(0x00200c0d))).size());
@ -545,7 +571,7 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
@Test @Test
public void testAddMappingThenRemoveButAbortThenTranslateTraceToStaticHitInMiddle() public void testAddMappingThenRemoveButAbortThenTranslateTraceToStaticHitInMiddle()
throws Exception { throws Throwable {
addMapping(); addMapping();
TraceLocation goodLoc = TraceLocation goodLoc =
new DefaultTraceLocation(tb.trace, null, Lifespan.nowOn(0), new DefaultTraceLocation(tb.trace, null, Lifespan.nowOn(0),
@ -553,19 +579,23 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
try (Transaction tx = tb.startTransaction()) { try (Transaction tx = tb.startTransaction()) {
mappingManager.findContaining(dynSpace.getAddress(0x00100000), 0).delete(); mappingManager.findContaining(dynSpace.getAddress(0x00100000), 0).delete();
waitForDomainObject(tb.trace); waitForDomainObject(tb.trace);
waitOn(mappingService.changesSettled());
// pre-check // pre-check
assertNull(mappingService.getOpenMappedLocation(goodLoc)); assertNull(mappingService.getOpenMappedLocation(goodLoc));
tx.abort(); tx.abort();
} }
waitForDomainObject(tb.trace); waitForDomainObject(tb.trace);
waitOn(mappingService.changesSettled());
assertNotNull(mappingService.getOpenMappedLocation(goodLoc)); assertNotNull(mappingService.getOpenMappedLocation(goodLoc));
} }
@Test @Test
public void testAddCorrelationRemoveButUndoThenRequestMappingDynamicToStaticWithin() public void testAddCorrelationRemoveButUndoThenRequestMappingDynamicToStaticWithin()
throws Exception { throws Throwable {
addMapping(); addMapping();
waitOn(mappingService.changesSettled());
TraceLocation goodLoc = TraceLocation goodLoc =
new DefaultTraceLocation(tb.trace, null, Lifespan.nowOn(0), new DefaultTraceLocation(tb.trace, null, Lifespan.nowOn(0),
dynSpace.getAddress(0x00100c0d)); dynSpace.getAddress(0x00100c0d));
@ -577,11 +607,13 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
mappingManager.findContaining(dynSpace.getAddress(0x00100000), 0).delete(); mappingManager.findContaining(dynSpace.getAddress(0x00100000), 0).delete();
} }
waitForDomainObject(tb.trace); waitForDomainObject(tb.trace);
waitOn(mappingService.changesSettled());
// pre-check // pre-check
assertNull(mappingService.getOpenMappedLocation(goodLoc)); assertNull(mappingService.getOpenMappedLocation(goodLoc));
undo(tb.trace, true); undo(tb.trace, true);
waitOn(mappingService.changesSettled());
assertNotNull(mappingService.getOpenMappedLocation(goodLoc)); assertNotNull(mappingService.getOpenMappedLocation(goodLoc));
} }
@ -619,7 +651,7 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
} }
@Test @Test
public void testMapFullSpace() throws Exception { public void testMapFullSpace() throws Throwable {
try (Transaction tx = tb.startTransaction()) { try (Transaction tx = tb.startTransaction()) {
TraceLocation traceLoc = TraceLocation traceLoc =
new DefaultTraceLocation(tb.trace, null, Lifespan.nowOn(0), tb.addr(0)); new DefaultTraceLocation(tb.trace, null, Lifespan.nowOn(0), tb.addr(0));
@ -627,7 +659,10 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
// NB. 0 indicates 1 << 64 // NB. 0 indicates 1 << 64
mappingService.addMapping(traceLoc, progLoc, 0, true); mappingService.addMapping(traceLoc, progLoc, 0, true);
} }
waitForPass(() -> assertMapsTwoWay(0L, 0L)); waitForSwing();
waitOn(mappingService.changesSettled());
assertMapsTwoWay(0L, 0L);
assertMapsTwoWay(-1L, -1L); assertMapsTwoWay(-1L, -1L);
assertMapsTwoWay(Long.MAX_VALUE, Long.MAX_VALUE); assertMapsTwoWay(Long.MAX_VALUE, Long.MAX_VALUE);
assertMapsTwoWay(Long.MIN_VALUE, Long.MIN_VALUE); assertMapsTwoWay(Long.MIN_VALUE, Long.MIN_VALUE);

View file

@ -33,7 +33,7 @@ import ghidra.util.database.*;
import ghidra.util.database.annot.*; import ghidra.util.database.annot.*;
/** /**
* The implementation of a stack mapping, directly via a database object * The implementation of a static mapping, directly via a database object
* *
* <p> * <p>
* Version history: * Version history:

View file

@ -26,7 +26,30 @@ import ghidra.program.model.listing.CodeUnit;
import ghidra.trace.model.TraceAddressSnapRange; import ghidra.trace.model.TraceAddressSnapRange;
import ghidra.util.AbstractPeekableIterator; import ghidra.util.AbstractPeekableIterator;
/**
* An iterator of overlapping objects return from two given iterators.
*
* <p>
* The given iterators, named left and right, must return objects each having a range attribute.
* Each iterator must return objects having disjoint ranges, i.e., no two objects from the
* <em>same</em> iterator may intersect. Each iterator must also return the objects sorted by min
* address. This iterator will then discover every case where an object from the left iterator
* overlaps an object from the right iterator, and return a pair for each such instance, in order of
* min address.
*
* <p>
* <b>WARNING:</b> To avoid heap pollution, this iterator re-uses the same {@link Pair} on each call
* to {@link #next()}. If you need to save an overlapping pair, you must copy it.
*
* @param <L> the type of objects returned by the left iterator
* @param <R> the type of objects returned by the right iterator
*/
public class OverlappingObjectIterator<L, R> extends AbstractPeekableIterator<Pair<L, R>> { public class OverlappingObjectIterator<L, R> extends AbstractPeekableIterator<Pair<L, R>> {
/**
* A means of obtaining the range attribute from each object
*
* @param <T> the type of objects returned by an iterator
*/
public interface Ranger<T> { public interface Ranger<T> {
Address getMinAddress(T t); Address getMinAddress(T t);

View file

@ -483,7 +483,7 @@ public abstract class AbstractDebuggerBreakpointMarkerPluginTest<T>
addMappedBreakpointOpenAndWait(); // wasteful, but whatever addMappedBreakpointOpenAndWait(); // wasteful, but whatever
for (LogicalBreakpoint lb : List.copyOf(breakpointService.getAllBreakpoints())) { for (LogicalBreakpoint lb : List.copyOf(breakpointService.getAllBreakpoints())) {
TraceBreakpoint brk = Unique.assertOne(lb.getTraceBreakpoints(tb.trace)); TraceBreakpoint brk = Unique.assertOne(lb.getTraceBreakpoints(tb.trace));
lb.delete(); waitOn(lb.delete());
handleDeleteBreakpointInvocation(brk); handleDeleteBreakpointInvocation(brk);
} }
waitForPass(() -> assertEquals(0, breakpointService.getAllBreakpoints().size())); waitForPass(() -> assertEquals(0, breakpointService.getAllBreakpoints().size()));
@ -515,7 +515,7 @@ public abstract class AbstractDebuggerBreakpointMarkerPluginTest<T>
addMappedBreakpointOpenAndWait(); // wasteful, but whatever addMappedBreakpointOpenAndWait(); // wasteful, but whatever
for (LogicalBreakpoint lb : List.copyOf(breakpointService.getAllBreakpoints())) { for (LogicalBreakpoint lb : List.copyOf(breakpointService.getAllBreakpoints())) {
TraceBreakpoint brk = Unique.assertOne(lb.getTraceBreakpoints(tb.trace)); TraceBreakpoint brk = Unique.assertOne(lb.getTraceBreakpoints(tb.trace));
lb.delete(); waitOn(lb.delete());
handleDeleteBreakpointInvocation(brk); handleDeleteBreakpointInvocation(brk);
} }
waitForPass(() -> assertEquals(0, breakpointService.getAllBreakpoints().size())); waitForPass(() -> assertEquals(0, breakpointService.getAllBreakpoints().size()));

View file

@ -46,8 +46,6 @@ import ghidra.trace.model.memory.TraceMemoryFlag;
import ghidra.trace.model.memory.TraceMemoryRegion; import ghidra.trace.model.memory.TraceMemoryRegion;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.util.SystemUtilities; import ghidra.util.SystemUtilities;
import utility.function.ExceptionalCallback;
import utility.function.ExceptionalSupplier;
public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR> public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
extends AbstractGhidraHeadedDebuggerIntegrationTest { extends AbstractGhidraHeadedDebuggerIntegrationTest {
@ -102,22 +100,6 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
} }
} }
protected <U, E extends Throwable> U expectMappingChange(ExceptionalSupplier<U, E> supplier)
throws Throwable {
mappingChangeListener.ar.set(false, null);
U result = supplier.get();
waitOn(mappingChangeListener.ar.waitValue(true));
return result;
}
protected <E extends Throwable> void expectMappingChange(ExceptionalCallback<E> runnable)
throws Throwable {
expectMappingChange(() -> {
runnable.call();
return null;
});
}
protected DebuggerStaticMappingService mappingService; protected DebuggerStaticMappingService mappingService;
protected DebuggerLogicalBreakpointService breakpointService; protected DebuggerLogicalBreakpointService breakpointService;
@ -134,8 +116,6 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
protected NoDuplicatesBreakpointsChangeListener changeListener = protected NoDuplicatesBreakpointsChangeListener changeListener =
new NoDuplicatesBreakpointsChangeListener(); new NoDuplicatesBreakpointsChangeListener();
protected ForTimingMappingChangeListener mappingChangeListener =
new ForTimingMappingChangeListener();
protected abstract void createTarget1() throws Throwable; protected abstract void createTarget1() throws Throwable;
@ -169,10 +149,12 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
protected abstract TraceBreakpoint findLoc(Set<TraceBreakpoint> locs, int index); protected abstract TraceBreakpoint findLoc(Set<TraceBreakpoint> locs, int index);
protected abstract void handleToggleBreakpointInvocation(TraceBreakpoint expectedBreakpoint, protected abstract void handleToggleBreakpointInvocation(T target,
TraceBreakpoint expectedBreakpoint,
boolean expectedEnabled) throws Throwable; boolean expectedEnabled) throws Throwable;
protected abstract void handleDeleteBreakpointInvocation(TraceBreakpoint expectedBreakpoint) protected abstract void handleDeleteBreakpointInvocation(T target,
TraceBreakpoint expectedBreakpoint)
throws Throwable; throws Throwable;
@Before @Before
@ -182,7 +164,6 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
mappingService = tool.getService(DebuggerStaticMappingService.class); mappingService = tool.getService(DebuggerStaticMappingService.class);
breakpointService.addChangeListener(changeListener); breakpointService.addChangeListener(changeListener);
mappingService.addChangeListener(mappingChangeListener);
} }
@After @After
@ -269,6 +250,7 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
.createInitializedBlock(".text", addr(p, 0x00400000), 0x1000, (byte) 0, monitor, .createInitializedBlock(".text", addr(p, 0x00400000), 0x1000, (byte) 0, monitor,
false); false);
} }
waitForDomainObject(p);
} }
protected void addProgramBreakpoints(Program p) throws Throwable { protected void addProgramBreakpoints(Program p) throws Throwable {
@ -280,6 +262,7 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
.setBookmark(addr(p, 0x00400321), LogicalBreakpoint.DISABLED_BOOKMARK_TYPE, .setBookmark(addr(p, 0x00400321), LogicalBreakpoint.DISABLED_BOOKMARK_TYPE,
"SW_EXECUTE;1", ""); "SW_EXECUTE;1", "");
} }
waitForDomainObject(p);
} }
protected void refetchProgramBreakpoints(Program p) throws Throwable { protected void refetchProgramBreakpoints(Program p) throws Throwable {
@ -297,6 +280,7 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
p.getBookmarkManager().removeBookmark(enBm); p.getBookmarkManager().removeBookmark(enBm);
p.getBookmarkManager().removeBookmark(disBm); p.getBookmarkManager().removeBookmark(disBm);
} }
waitForDomainObject(p);
} }
protected void assertLogicalBreakpointForLoneAccessBreakpoint(Trace trace) { protected void assertLogicalBreakpointForLoneAccessBreakpoint(Trace trace) {
@ -546,8 +530,8 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
addProgramTextBlock(program); addProgramTextBlock(program);
MR text = addTargetTextRegion(target1); MR text = addTargetTextRegion(target1);
expectMappingChange(() -> addTextMapping(target1, text, program)); addTextMapping(target1, text, program);
waitForSwing(); waitOn(mappingService.changesSettled());
assertTrue(breakpointService.getAllBreakpoints().isEmpty()); assertTrue(breakpointService.getAllBreakpoints().isEmpty());
} }
@ -564,9 +548,9 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
addProgramTextBlock(program); addProgramTextBlock(program);
MR text = addTargetTextRegion(target1); MR text = addTargetTextRegion(target1);
expectMappingChange(() -> addTextMapping(target1, text, program)); addTextMapping(target1, text, program);
waitOn(mappingService.changesSettled());
addProgramBreakpoints(program); addProgramBreakpoints(program);
waitForSwing();
assertLogicalBreakpointsForMappedBookmarks(trace); assertLogicalBreakpointsForMappedBookmarks(trace);
} }
@ -584,12 +568,12 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
addProgramTextBlock(program); addProgramTextBlock(program);
MR text = addTargetTextRegion(target1); MR text = addTargetTextRegion(target1);
addProgramBreakpoints(program); addProgramBreakpoints(program);
waitForSwing();
changeListener.assertAgreesWithService(); changeListener.assertAgreesWithService();
expectMappingChange(() -> addTextMapping(target1, text, program)); addTextMapping(target1, text, program);
waitForSwing(); waitOn(mappingService.changesSettled());
waitOn(breakpointService.changesSettled());
assertLogicalBreakpointsForMappedBookmarks(trace); assertLogicalBreakpointsForMappedBookmarks(trace);
} }
@ -606,12 +590,12 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
addProgramTextBlock(program); addProgramTextBlock(program);
MR text = addTargetTextRegion(target1); MR text = addTargetTextRegion(target1);
expectMappingChange(() -> addTextMapping(target1, text, program)); addTextMapping(target1, text, program);
addTargetSoftwareBreakpoint(target1, text); addTargetSoftwareBreakpoint(target1, text);
waitOn(mappingService.changesSettled());
waitOn(breakpointService.changesSettled());
waitForPass(() -> { assertLogicalBreakpointForMappedSoftwareBreakpoint(trace);
assertLogicalBreakpointForMappedSoftwareBreakpoint(trace);
});
} }
@Test @Test
@ -627,14 +611,14 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
addProgramTextBlock(program); addProgramTextBlock(program);
MR text = addTargetTextRegion(target1); MR text = addTargetTextRegion(target1);
addTargetSoftwareBreakpoint(target1, text); addTargetSoftwareBreakpoint(target1, text);
waitOn(breakpointService.changesSettled());
waitForPass(() -> { assertLogicalBreakpointForLoneSoftwareBreakpoint(trace, 1);
assertLogicalBreakpointForLoneSoftwareBreakpoint(trace, 1);
});
changeListener.assertAgreesWithService(); changeListener.assertAgreesWithService();
expectMappingChange(() -> addTextMapping(target1, text, program)); addTextMapping(target1, text, program);
waitForSwing(); waitOn(mappingService.changesSettled());
waitOn(breakpointService.changesSettled());
assertLogicalBreakpointForMappedSoftwareBreakpoint(trace); assertLogicalBreakpointForMappedSoftwareBreakpoint(trace);
} }
@ -660,8 +644,9 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
// NOTE: Extraneous mappings-changed events can cause timing issues here. // NOTE: Extraneous mappings-changed events can cause timing issues here.
// TODO: Better testing for static mapping listener events? // TODO: Better testing for static mapping listener events?
MR text = addTargetTextRegion(target1); MR text = addTargetTextRegion(target1);
expectMappingChange(() -> addTextMapping(target1, text, program)); addTextMapping(target1, text, program);
waitForSwing(); waitOn(mappingService.changesSettled());
waitOn(breakpointService.changesSettled());
assertLogicalBreakpointsForMappedBookmarks(trace); assertLogicalBreakpointsForMappedBookmarks(trace);
} }
@ -680,12 +665,15 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
MR text = addTargetTextRegion(target1); MR text = addTargetTextRegion(target1);
addTextMapping(target1, text, program); addTextMapping(target1, text, program);
addProgramBreakpoints(program); addProgramBreakpoints(program);
waitForSwing(); waitOn(mappingService.changesSettled());
waitOn(breakpointService.changesSettled());
assertTrue(breakpointService.getAllBreakpoints().isEmpty()); assertTrue(breakpointService.getAllBreakpoints().isEmpty());
expectMappingChange(() -> programManager.openProgram(program)); programManager.openProgram(program);
waitForSwing(); waitForDomainObject(program);
waitOn(mappingService.changesSettled());
waitOn(breakpointService.changesSettled());
assertLogicalBreakpointsForMappedBookmarks(trace); assertLogicalBreakpointsForMappedBookmarks(trace);
} }
@ -704,13 +692,17 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
MR text = addTargetTextRegion(target1); MR text = addTargetTextRegion(target1);
addTextMapping(target1, text, program); addTextMapping(target1, text, program);
addTargetSoftwareBreakpoint(target1, text); addTargetSoftwareBreakpoint(target1, text);
waitOn(mappingService.changesSettled());
waitOn(breakpointService.changesSettled());
waitForPass(() -> assertLogicalBreakpointForLoneSoftwareBreakpoint(trace, 1)); assertLogicalBreakpointForLoneSoftwareBreakpoint(trace, 1);
expectMappingChange(() -> programManager.openProgram(program)); programManager.openProgram(program);
waitForSwing(); waitForDomainObject(program);
waitOn(mappingService.changesSettled());
waitOn(breakpointService.changesSettled());
waitForPass(() -> assertLogicalBreakpointForMappedSoftwareBreakpoint(trace)); assertLogicalBreakpointForMappedSoftwareBreakpoint(trace);
} }
@Test @Test
@ -722,12 +714,16 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
createProgramFromTrace(trace); createProgramFromTrace(trace);
intoProject(program); intoProject(program);
programManager.openProgram(program); programManager.openProgram(program);
waitForSwing(); waitForDomainObject(program);
waitOn(mappingService.changesSettled());
waitOn(breakpointService.changesSettled());
assertTrue(breakpointService.getAllBreakpoints().isEmpty()); assertTrue(breakpointService.getAllBreakpoints().isEmpty());
programManager.closeProgram(program, true); programManager.closeProgram(program, true);
waitForSwing(); waitForSwing();
waitOn(mappingService.changesSettled());
waitOn(breakpointService.changesSettled());
assertTrue(breakpointService.getAllBreakpoints().isEmpty()); assertTrue(breakpointService.getAllBreakpoints().isEmpty());
} }
@ -741,13 +737,17 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
createProgramFromTrace(trace); createProgramFromTrace(trace);
intoProject(program); intoProject(program);
programManager.openProgram(program); programManager.openProgram(program);
waitForSwing(); waitForDomainObject(program);
waitOn(mappingService.changesSettled());
waitOn(breakpointService.changesSettled());
assertTrue(breakpointService.getAllBreakpoints().isEmpty()); assertTrue(breakpointService.getAllBreakpoints().isEmpty());
traceManager.closeTrace(trace); traceManager.closeTrace(trace);
terminateTarget(target1); terminateTarget(target1);
waitForSwing(); waitForSwing();
waitOn(mappingService.changesSettled());
waitOn(breakpointService.changesSettled());
assertTrue(breakpointService.getAllBreakpoints().isEmpty()); assertTrue(breakpointService.getAllBreakpoints().isEmpty());
} }
@ -766,13 +766,14 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
addProgramTextBlock(program); addProgramTextBlock(program);
addProgramBreakpoints(program); addProgramBreakpoints(program);
MR text = addTargetTextRegion(target1); MR text = addTargetTextRegion(target1);
expectMappingChange(() -> addTextMapping(target1, text, program)); addTextMapping(target1, text, program);
waitForSwing(); waitOn(mappingService.changesSettled());
waitOn(breakpointService.changesSettled());
assertLogicalBreakpointsForMappedBookmarks(trace); assertLogicalBreakpointsForMappedBookmarks(trace);
removeProgramBreakpoints(program); removeProgramBreakpoints(program);
waitForSwing(); waitOn(breakpointService.changesSettled());
assertTrue(breakpointService.getAllBreakpoints().isEmpty()); assertTrue(breakpointService.getAllBreakpoints().isEmpty());
} }
@ -791,13 +792,15 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
addProgramTextBlock(program); addProgramTextBlock(program);
addProgramBreakpoints(program); addProgramBreakpoints(program);
MR text = addTargetTextRegion(target1); MR text = addTargetTextRegion(target1);
expectMappingChange(() -> addTextMapping(target1, text, program)); addTextMapping(target1, text, program);
waitForSwing(); waitOn(mappingService.changesSettled());
waitOn(breakpointService.changesSettled());
assertLogicalBreakpointsForMappedBookmarks(trace); assertLogicalBreakpointsForMappedBookmarks(trace);
expectMappingChange(() -> removeTextMapping(target1, program)); removeTextMapping(target1, program);
waitForSwing(); waitOn(mappingService.changesSettled());
waitOn(breakpointService.changesSettled());
assertLogicalBreakpointsForUnmappedBookmarks(); assertLogicalBreakpointsForUnmappedBookmarks();
assertTrue(breakpointService.getBreakpoints(trace).isEmpty()); assertTrue(breakpointService.getBreakpoints(trace).isEmpty());
@ -818,19 +821,19 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
programManager.openProgram(program); programManager.openProgram(program);
addProgramTextBlock(program); addProgramTextBlock(program);
expectMappingChange(() -> addTextMapping(target1, text, program)); addTextMapping(target1, text, program);
waitForSwing(); waitOn(mappingService.changesSettled());
waitOn(breakpointService.changesSettled());
assertLogicalBreakpointForMappedSoftwareBreakpoint(trace); assertLogicalBreakpointForMappedSoftwareBreakpoint(trace);
assertServiceAgreesWithOpenProgramsAndTraces(); assertServiceAgreesWithOpenProgramsAndTraces();
removeTargetSoftwareBreakpoint(target1); removeTargetSoftwareBreakpoint(target1);
waitOn(breakpointService.changesSettled());
waitForPass(() -> { // NB. The bookmark remains
// NB. The bookmark remains LogicalBreakpoint one = Unique.assertOne(breakpointService.getAllBreakpoints());
LogicalBreakpoint one = Unique.assertOne(breakpointService.getAllBreakpoints()); assertTrue(one.getTraceBreakpoints().isEmpty());
assertTrue(one.getTraceBreakpoints().isEmpty());
});
} }
@Test @Test
@ -848,14 +851,16 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
programManager.openProgram(program); programManager.openProgram(program);
addProgramTextBlock(program); addProgramTextBlock(program);
expectMappingChange(() -> addTextMapping(target1, text, program)); addTextMapping(target1, text, program);
waitForSwing(); waitOn(mappingService.changesSettled());
waitOn(breakpointService.changesSettled());
assertLogicalBreakpointForMappedSoftwareBreakpoint(trace); assertLogicalBreakpointForMappedSoftwareBreakpoint(trace);
assertServiceAgreesWithOpenProgramsAndTraces(); assertServiceAgreesWithOpenProgramsAndTraces();
expectMappingChange(() -> removeTextMapping(target1, program)); removeTextMapping(target1, program);
waitForSwing(); waitOn(mappingService.changesSettled());
waitOn(breakpointService.changesSettled());
// NB. Bookmark remains // NB. Bookmark remains
assertLogicalBreakpointForLoneSoftwareBreakpoint(trace, 2); assertLogicalBreakpointForLoneSoftwareBreakpoint(trace, 2);
@ -881,23 +886,19 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
addTextMapping(target1, text1, program); addTextMapping(target1, text1, program);
addTextMapping(target3, text3, program); addTextMapping(target3, text3, program);
waitForSwing(); waitOn(mappingService.changesSettled());
waitForPass(() -> { waitOn(breakpointService.changesSettled());
assertEquals(2,
mappingService assertEquals(2, mappingService
.getOpenMappedLocations( .getOpenMappedLocations(new ProgramLocation(program, addr(program, 0x00400123)))
new ProgramLocation(program, addr(program, 0x00400123))) .size());
.size());
});
waitForSwing();
addProgramBreakpoints(program); addProgramBreakpoints(program);
addTargetSoftwareBreakpoint(target1, text1); addTargetSoftwareBreakpoint(target1, text1);
addTargetSoftwareBreakpoint(target3, text3); addTargetSoftwareBreakpoint(target3, text3);
waitOn(breakpointService.changesSettled());
waitForPass(() -> { assertLogicalBreakpointForMappedBookmarkAnd2TraceBreakpoints(trace1, trace3);
assertLogicalBreakpointForMappedBookmarkAnd2TraceBreakpoints(trace1, trace3);
});
} }
@Test @Test
@ -920,26 +921,24 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
addTextMapping(target1, text1, program); addTextMapping(target1, text1, program);
addTextMapping(target3, text3, program); addTextMapping(target3, text3, program);
waitForSwing(); waitOn(mappingService.changesSettled());
waitForPass(() -> { waitOn(breakpointService.changesSettled());
assertEquals(2,
mappingService assertEquals(2, mappingService
.getOpenMappedLocations( .getOpenMappedLocations(new ProgramLocation(program, addr(program, 0x00400123)))
new ProgramLocation(program, addr(program, 0x00400123))) .size());
.size());
});
waitForSwing();
addProgramBreakpoints(program); addProgramBreakpoints(program);
addTargetSoftwareBreakpoint(target1, text1); addTargetSoftwareBreakpoint(target1, text1);
addTargetSoftwareBreakpoint(target3, text3); addTargetSoftwareBreakpoint(target3, text3);
waitOn(breakpointService.changesSettled());
waitForPass(() -> { assertLogicalBreakpointForMappedBookmarkAnd2TraceBreakpoints(trace1, trace3);
assertLogicalBreakpointForMappedBookmarkAnd2TraceBreakpoints(trace1, trace3);
});
expectMappingChange(() -> programManager.closeProgram(program, true)); programManager.closeProgram(program, true);
waitForSwing(); waitForSwing();
waitOn(mappingService.changesSettled());
waitOn(breakpointService.changesSettled());
assertLogicalBreakpointForLoneSoftwareBreakpoint(trace1, 0x55550123, 2); assertLogicalBreakpointForLoneSoftwareBreakpoint(trace1, 0x55550123, 2);
assertLogicalBreakpointForLoneSoftwareBreakpoint(trace3, 0x55551123, 2); assertLogicalBreakpointForLoneSoftwareBreakpoint(trace3, 0x55551123, 2);
@ -965,38 +964,34 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
addTextMapping(target1, text1, program); addTextMapping(target1, text1, program);
addTextMapping(target3, text3, program); addTextMapping(target3, text3, program);
waitForSwing(); waitOn(mappingService.changesSettled());
waitForPass(() -> { waitOn(breakpointService.changesSettled());
assertEquals(2,
mappingService assertEquals(2, mappingService
.getOpenMappedLocations( .getOpenMappedLocations(new ProgramLocation(program, addr(program, 0x00400123)))
new ProgramLocation(program, addr(program, 0x00400123))) .size());
.size());
});
waitForSwing();
addProgramBreakpoints(program); addProgramBreakpoints(program);
addTargetSoftwareBreakpoint(target1, text1); addTargetSoftwareBreakpoint(target1, text1);
addTargetSoftwareBreakpoint(target3, text3); addTargetSoftwareBreakpoint(target3, text3);
waitOn(breakpointService.changesSettled());
waitForPass(() -> { assertLogicalBreakpointForMappedBookmarkAnd2TraceBreakpoints(trace1, trace3);
assertLogicalBreakpointForMappedBookmarkAnd2TraceBreakpoints(trace1, trace3);
});
expectMappingChange(() -> programManager.closeProgram(program, true)); programManager.closeProgram(program, true);
waitForSwing(); waitForSwing();
waitOn(mappingService.changesSettled());
waitOn(breakpointService.changesSettled());
waitForPass(() -> { assertLogicalBreakpointForLoneSoftwareBreakpoint(trace1, 0x55550123, 2);
assertLogicalBreakpointForLoneSoftwareBreakpoint(trace1, 0x55550123, 2); assertLogicalBreakpointForLoneSoftwareBreakpoint(trace3, 0x55551123, 2);
assertLogicalBreakpointForLoneSoftwareBreakpoint(trace3, 0x55551123, 2);
});
expectMappingChange(() -> programManager.openProgram(program)); programManager.openProgram(program);
waitForSwing(); waitForDomainObject(program);
waitOn(mappingService.changesSettled());
waitOn(breakpointService.changesSettled());
waitForPass(() -> { assertLogicalBreakpointForMappedBookmarkAnd2TraceBreakpoints(trace1, trace3);
assertLogicalBreakpointForMappedBookmarkAnd2TraceBreakpoints(trace1, trace3);
});
} }
@Test @Test
@ -1019,36 +1014,34 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
addTextMapping(target1, text1, program); addTextMapping(target1, text1, program);
addTextMapping(target3, text3, program); addTextMapping(target3, text3, program);
waitForSwing(); waitOn(mappingService.changesSettled());
waitForPass(() -> { waitOn(breakpointService.changesSettled());
assertEquals(2,
mappingService assertEquals(2, mappingService
.getOpenMappedLocations( .getOpenMappedLocations(new ProgramLocation(program, addr(program, 0x00400123)))
new ProgramLocation(program, addr(program, 0x00400123))) .size());
.size());
});
waitForSwing();
addProgramBreakpoints(program); addProgramBreakpoints(program);
addTargetSoftwareBreakpoint(target1, text1); addTargetSoftwareBreakpoint(target1, text1);
addTargetSoftwareBreakpoint(target3, text3); addTargetSoftwareBreakpoint(target3, text3);
waitForSwing(); waitOn(breakpointService.changesSettled());
waitForPass(() -> { assertLogicalBreakpointForMappedBookmarkAnd2TraceBreakpoints(trace1, trace3);
assertLogicalBreakpointForMappedBookmarkAnd2TraceBreakpoints(trace1, trace3);
});
waitForLock(getTrace(target3)); waitForLock(getTrace(target3));
expectMappingChange(() -> { /**
// If I don't close the trace here, the test will fail. * NB. Relying on terminate to auto-close is causing some timing issue I don't yet
terminateTarget(target3); * understand. So, I close it in the test code.
// NB. Auto-close on stop is the default */
//traceManager.closeTrace(trace3); terminateTarget(target3);
}); traceManager.closeTrace(trace3);
waitForPass(() -> !traceManager.getOpenTraces().contains(trace3));
waitForSwing(); waitForSwing();
waitOn(mappingService.changesSettled());
waitOn(breakpointService.changesSettled());
// NB. Auto-close is possibly delayed because of auto-save // NB. Auto-close is possibly delayed because of auto-save
waitForPass(() -> assertLogicalBreakpointForMappedBookmarkAnd1TraceBreakpoint(trace1)); assertLogicalBreakpointForMappedBookmarkAnd1TraceBreakpoint(trace1);
} }
@Test // Mappings are not write-behind cached @Test // Mappings are not write-behind cached
@ -1064,10 +1057,10 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
addProgramTextBlock(program); addProgramTextBlock(program);
MR text = addTargetTextRegion(target1); MR text = addTargetTextRegion(target1);
addTargetSoftwareBreakpoint(target1, text); addTargetSoftwareBreakpoint(target1, text);
waitOn(mappingService.changesSettled());
waitOn(breakpointService.changesSettled());
waitForPass(() -> { assertLogicalBreakpointForLoneSoftwareBreakpoint(trace, 1);
assertLogicalBreakpointForLoneSoftwareBreakpoint(trace, 1);
});
/** /**
* NB. The target could still be mid transaction. If we open this transaction too soon, then * NB. The target could still be mid transaction. If we open this transaction too soon, then
* the breakpoint gets aborted as well. * the breakpoint gets aborted as well.
@ -1077,19 +1070,21 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
changeListener.assertAgreesWithService(); changeListener.assertAgreesWithService();
try (Transaction tx = trace.openTransaction("Will abort")) { try (Transaction tx = trace.openTransaction("Will abort")) {
expectMappingChange(() -> addTextMapping(target1, text, program)); addTextMapping(target1, text, program);
waitForSwing(); waitOn(mappingService.changesSettled());
waitOn(breakpointService.changesSettled());
// Sanity // Sanity
assertLogicalBreakpointForMappedSoftwareBreakpoint(trace); assertLogicalBreakpointForMappedSoftwareBreakpoint(trace);
expectMappingChange(() -> tx.abort()); tx.abort();
} }
waitForDomainObject(trace);
waitOn(mappingService.changesSettled());
waitOn(breakpointService.changesSettled());
waitForPass(() -> { // NB. The bookmark is left over, so total increases
// NB. The bookmark is left over, so total increases assertLogicalBreakpointForLoneSoftwareBreakpoint(trace, 2);
assertLogicalBreakpointForLoneSoftwareBreakpoint(trace, 2);
});
} }
// @Test // Not gonna with write-behind cache // @Test // Not gonna with write-behind cache
@ -1108,15 +1103,18 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
try (Transaction tx = trace.openTransaction("Will abort")) { try (Transaction tx = trace.openTransaction("Will abort")) {
addTargetSoftwareBreakpoint(target1, text); addTargetSoftwareBreakpoint(target1, text);
expectMappingChange(() -> addTextMapping(target1, text, program)); addTextMapping(target1, text, program);
waitForSwing(); waitOn(mappingService.changesSettled());
waitOn(breakpointService.changesSettled());
// Sanity // Sanity
assertLogicalBreakpointForMappedSoftwareBreakpoint(trace); assertLogicalBreakpointForMappedSoftwareBreakpoint(trace);
expectMappingChange(() -> tx.abort()); tx.abort();
} }
waitForDomainObject(trace); // Duplicative, but for form's sake.... waitForDomainObject(trace);
waitOn(mappingService.changesSettled());
waitOn(breakpointService.changesSettled());
// Left over, because it was bookmarked automatically in program // Left over, because it was bookmarked automatically in program
// Still, there should be no trace breakpoint in it // Still, there should be no trace breakpoint in it
@ -1136,18 +1134,19 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
addProgramTextBlock(program); addProgramTextBlock(program);
MR text = addTargetTextRegion(target1); MR text = addTargetTextRegion(target1);
expectMappingChange(() -> addTextMapping(target1, text, program)); addTextMapping(target1, text, program);
waitForSwing(); waitOn(mappingService.changesSettled());
waitOn(breakpointService.changesSettled());
try (Transaction tx = program.openTransaction("Will abort")) { try (Transaction tx = program.openTransaction("Will abort")) {
addProgramBreakpoints(program); addProgramBreakpoints(program);
waitForDomainObject(program);
// Sanity // Sanity
assertLogicalBreakpointsForMappedBookmarks(trace); assertLogicalBreakpointsForMappedBookmarks(trace);
tx.abort(); tx.abort();
} }
waitForDomainObject(program); waitForDomainObject(program);
waitOn(breakpointService.changesSettled());
assertTrue(breakpointService.getAllBreakpoints().isEmpty()); assertTrue(breakpointService.getAllBreakpoints().isEmpty());
} }
@ -1167,18 +1166,16 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
try (Transaction tx = trace.openTransaction("Will undo")) { try (Transaction tx = trace.openTransaction("Will undo")) {
addTargetSoftwareBreakpoint(target1, text); addTargetSoftwareBreakpoint(target1, text);
expectMappingChange(() -> addTextMapping(target1, text, program)); addTextMapping(target1, text, program);
} }
waitForDomainObject(trace); waitForDomainObject(program);
waitOn(mappingService.changesSettled()); waitOn(mappingService.changesSettled());
waitOn(breakpointService.changesSettled()); waitOn(breakpointService.changesSettled());
// Sanity // Sanity
assertLogicalBreakpointForMappedSoftwareBreakpoint(trace); assertLogicalBreakpointForMappedSoftwareBreakpoint(trace);
expectMappingChange(() -> undo(trace)); undo(trace);
waitOn(mappingService.changesSettled()); waitOn(mappingService.changesSettled());
waitOn(breakpointService.changesSettled()); waitOn(breakpointService.changesSettled());
@ -1186,10 +1183,12 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
LogicalBreakpoint one = Unique.assertOne(breakpointService.getAllBreakpoints()); LogicalBreakpoint one = Unique.assertOne(breakpointService.getAllBreakpoints());
assertTrue(one.getTraceBreakpoints().isEmpty()); assertTrue(one.getTraceBreakpoints().isEmpty());
expectMappingChange(() -> redo(trace)); redo(trace);
waitOn(mappingService.changesSettled());
waitOn(breakpointService.changesSettled());
// Mapping, breakpoint may be processed in whatever order // Mapping, breakpoint may be processed in whatever order
waitForPass(() -> assertLogicalBreakpointForMappedSoftwareBreakpoint(trace)); assertLogicalBreakpointForMappedSoftwareBreakpoint(trace);
} }
@Test @Test
@ -1204,22 +1203,27 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
addProgramTextBlock(program); addProgramTextBlock(program);
MR text = addTargetTextRegion(target1); MR text = addTargetTextRegion(target1);
expectMappingChange(() -> addTextMapping(target1, text, program)); addTextMapping(target1, text, program);
waitForSwing(); waitOn(mappingService.changesSettled());
waitOn(breakpointService.changesSettled());
try (Transaction tx = program.openTransaction("Will undo")) { try (Transaction tx = program.openTransaction("Will undo")) {
addProgramBreakpoints(program); addProgramBreakpoints(program);
} }
waitForDomainObject(program); waitForDomainObject(program);
waitOn(mappingService.changesSettled());
waitOn(breakpointService.changesSettled());
// Sanity // Sanity
assertLogicalBreakpointsForMappedBookmarks(trace); assertLogicalBreakpointsForMappedBookmarks(trace);
undo(program); undo(program);
waitOn(breakpointService.changesSettled());
assertTrue(breakpointService.getAllBreakpoints().isEmpty()); assertTrue(breakpointService.getAllBreakpoints().isEmpty());
redo(program); redo(program);
waitOn(breakpointService.changesSettled());
refetchProgramBreakpoints(program); refetchProgramBreakpoints(program);
assertLogicalBreakpointsForMappedBookmarks(trace); assertLogicalBreakpointsForMappedBookmarks(trace);
@ -1234,18 +1238,20 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
MR text = addTargetTextRegion(target1); MR text = addTargetTextRegion(target1);
addTargetSoftwareBreakpoint(target1, text); addTargetSoftwareBreakpoint(target1, text);
waitForPass(() -> { waitOn(breakpointService.changesSettled());
assertLogicalBreakpointForLoneSoftwareBreakpoint(trace, 1);
}); assertLogicalBreakpointForLoneSoftwareBreakpoint(trace, 1);
LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints()); LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints());
CompletableFuture<Void> disable = lb.disable(); CompletableFuture<Void> disable = lb.disable();
handleToggleBreakpointInvocation(Unique.assertOne(lb.getTraceBreakpoints(trace)), false); handleToggleBreakpointInvocation(target1, Unique.assertOne(lb.getTraceBreakpoints(trace)),
false);
waitOn(disable); waitOn(disable);
waitForPass(() -> { waitForDomainObject(trace);
assertEquals(State.INCONSISTENT_DISABLED, lb.computeState()); waitOn(breakpointService.changesSettled());
});
assertEquals(State.INCONSISTENT_DISABLED, lb.computeState());
// Simulate a step, which should also cause snap advance in target // Simulate a step, which should also cause snap advance in target
long oldSnap = getSnap(target1); long oldSnap = getSnap(target1);
@ -1256,11 +1262,13 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
}); });
CompletableFuture<Void> enable = lb.enable(); CompletableFuture<Void> enable = lb.enable();
handleToggleBreakpointInvocation(Unique.assertOne(lb.getTraceBreakpoints(trace)), true); handleToggleBreakpointInvocation(target1, Unique.assertOne(lb.getTraceBreakpoints(trace)),
true);
waitOn(enable); waitOn(enable);
waitForPass(() -> { waitForDomainObject(trace);
assertEquals(State.INCONSISTENT_ENABLED, lb.computeState()); waitOn(breakpointService.changesSettled());
});
assertEquals(State.INCONSISTENT_ENABLED, lb.computeState());
} }
@Test @Test
@ -1270,22 +1278,20 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
traceManager.openTrace(trace); traceManager.openTrace(trace);
MR text = addTargetTextRegion(target1); MR text = addTargetTextRegion(target1);
addTargetSoftwareBreakpoint(target1, text); addTargetSoftwareBreakpoint(target1, text);
waitOn(breakpointService.changesSettled());
waitForPass(() -> { assertLogicalBreakpointForLoneSoftwareBreakpoint(trace, 1);
assertLogicalBreakpointForLoneSoftwareBreakpoint(trace, 1);
});
LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints()); LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints());
CompletableFuture<Void> delete = lb.delete(); CompletableFuture<Void> delete = lb.delete();
handleDeleteBreakpointInvocation(Unique.assertOne(lb.getTraceBreakpoints(trace))); handleDeleteBreakpointInvocation(target1, Unique.assertOne(lb.getTraceBreakpoints(trace)));
waitOn(delete); waitOn(delete);
waitForDomainObject(trace);
waitOn(breakpointService.changesSettled());
waitForPass(() -> { assertTrue(breakpointService.getAllBreakpoints().isEmpty());
assertTrue(breakpointService.getAllBreakpoints().isEmpty());
});
} }
@Test @Test
@ -1295,11 +1301,10 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
traceManager.openTrace(trace); traceManager.openTrace(trace);
MR text = addTargetTextRegion(target1); MR text = addTargetTextRegion(target1);
addTargetSoftwareBreakpoint(target1, text); addTargetSoftwareBreakpoint(target1, text);
waitForDomainObject(trace); waitOn(breakpointService.changesSettled());
waitForPass(() -> assertLogicalBreakpointForLoneSoftwareBreakpoint(trace, 1)); assertLogicalBreakpointForLoneSoftwareBreakpoint(trace, 1);
LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints()); LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints());
@ -1307,12 +1312,12 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
simulateTargetStep(target1); simulateTargetStep(target1);
CompletableFuture<Void> delete = lb.delete(); CompletableFuture<Void> delete = lb.delete();
handleDeleteBreakpointInvocation(Unique.assertOne(lb.getTraceBreakpoints(trace))); handleDeleteBreakpointInvocation(target1, Unique.assertOne(lb.getTraceBreakpoints(trace)));
waitOn(delete); waitOn(delete);
waitForDomainObject(trace);
waitOn(breakpointService.changesSettled());
waitForPass(() -> { assertEquals(0, breakpointService.getAllBreakpoints().size());
assertEquals(0, breakpointService.getAllBreakpoints().size());
});
} }
@Test @Test
@ -1324,14 +1329,14 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
MR text = addTargetTextRegion(target1); MR text = addTargetTextRegion(target1);
addTargetSoftwareBreakpoint(target1, text); addTargetSoftwareBreakpoint(target1, text);
waitOn(breakpointService.changesSettled());
waitForPass(() -> { assertLogicalBreakpointForLoneSoftwareBreakpoint(trace, 1);
assertLogicalBreakpointForLoneSoftwareBreakpoint(trace, 1);
});
// NOTE: Still recording in the background // NOTE: Still recording in the background
traceManager.closeTraceNoConfirm(trace); traceManager.closeTraceNoConfirm(trace);
waitForSwing(); waitForSwing();
waitOn(breakpointService.changesSettled());
assertEquals(0, breakpointService.getAllBreakpoints().size()); assertEquals(0, breakpointService.getAllBreakpoints().size());
} }
@ -1350,39 +1355,36 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
MR text = addTargetTextRegion(target1); MR text = addTargetTextRegion(target1);
addTextMapping(target1, text, program); addTextMapping(target1, text, program);
waitForSwing();
addProgramBreakpoints(program); addProgramBreakpoints(program);
addTargetSoftwareBreakpoint(target1, text); addTargetSoftwareBreakpoint(target1, text);
addTargetSoftwareBreakpoint(target1, text); addTargetSoftwareBreakpoint(target1, text);
waitOn(mappingService.changesSettled());
waitOn(breakpointService.changesSettled());
waitForPass(() -> { assertEquals(2, breakpointService.getAllBreakpoints().size());
assertEquals(2, breakpointService.getAllBreakpoints().size());
LogicalBreakpoint lb = Unique.assertOne( LogicalBreakpoint lb = Unique.assertOne(
breakpointService.getBreakpointsAt(program, addr(program, 0x00400123))); breakpointService.getBreakpointsAt(program, addr(program, 0x00400123)));
assertEquals(program, lb.getProgram()); assertEquals(program, lb.getProgram());
assertEquals(Set.of(trace), lb.getMappedTraces()); assertEquals(Set.of(trace), lb.getMappedTraces());
assertEquals(2, lb.getTraceBreakpoints().size()); assertEquals(2, lb.getTraceBreakpoints().size());
});
LogicalBreakpoint lb = Unique
.assertOne(breakpointService.getBreakpointsAt(program, addr(program, 0x00400123)));
Set<TraceBreakpoint> locs = lb.getTraceBreakpoints(); Set<TraceBreakpoint> locs = lb.getTraceBreakpoints();
TraceBreakpoint bpt0 = findLoc(locs, 0); TraceBreakpoint bpt0 = findLoc(locs, 0);
TraceBreakpoint bpt1 = findLoc(locs, 1); TraceBreakpoint bpt1 = findLoc(locs, 1);
CompletableFuture<Void> disable = breakpointService.disableLocs(Set.of(bpt0)); CompletableFuture<Void> disable = breakpointService.disableLocs(Set.of(bpt0));
handleToggleBreakpointInvocation(bpt0, false); handleToggleBreakpointInvocation(target1, bpt0, false);
waitOn(disable); waitOn(disable);
waitForDomainObject(trace);
waitOn(breakpointService.changesSettled());
waitForPass(() -> { assertEquals(State.INCONSISTENT_ENABLED, lb.computeState());
assertEquals(State.INCONSISTENT_ENABLED, lb.computeState()); assertEquals(State.INCONSISTENT_MIXED, lb.computeStateForTrace(trace));
assertEquals(State.INCONSISTENT_MIXED, lb.computeStateForTrace(trace)); assertEquals(State.INCONSISTENT_DISABLED, lb.computeStateForLocation(bpt0));
assertEquals(State.INCONSISTENT_DISABLED, lb.computeStateForLocation(bpt0)); assertEquals(State.ENABLED, lb.computeStateForLocation(bpt1));
assertEquals(State.ENABLED, lb.computeStateForLocation(bpt1));
});
} }
@Test @Test
@ -1399,29 +1401,26 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
MR text = addTargetTextRegion(target1); MR text = addTargetTextRegion(target1);
addTextMapping(target1, text, program); addTextMapping(target1, text, program);
waitForSwing();
addProgramBreakpoints(program); addProgramBreakpoints(program);
addTargetSoftwareBreakpoint(target1, text); addTargetSoftwareBreakpoint(target1, text);
addTargetAccessBreakpoint(target1, text); addTargetAccessBreakpoint(target1, text);
waitOn(mappingService.changesSettled());
waitOn(breakpointService.changesSettled());
waitForPass(() -> { assertEquals(3, breakpointService.getAllBreakpoints().size());
assertEquals(3, breakpointService.getAllBreakpoints().size());
Set<LogicalBreakpoint> lbs =
breakpointService.getBreakpointsAt(program, addr(program, 0x00400123));
assertEquals(2, lbs.size());
lbs.stream()
.filter(l -> l.getKinds().contains(TraceBreakpointKind.SW_EXECUTE))
.findAny()
.orElseThrow();
lbs.stream()
.filter(l -> l.getKinds().contains(TraceBreakpointKind.READ))
.findAny()
.orElseThrow();
});
Set<LogicalBreakpoint> lbs = Set<LogicalBreakpoint> lbs =
breakpointService.getBreakpointsAt(program, addr(program, 0x00400123)); breakpointService.getBreakpointsAt(program, addr(program, 0x00400123));
assertEquals(2, lbs.size());
lbs.stream()
.filter(l -> l.getKinds().contains(TraceBreakpointKind.SW_EXECUTE))
.findAny()
.orElseThrow();
lbs.stream()
.filter(l -> l.getKinds().contains(TraceBreakpointKind.READ))
.findAny()
.orElseThrow();
LogicalBreakpoint lbEx = lbs.stream() LogicalBreakpoint lbEx = lbs.stream()
.filter(l -> l.getKinds().contains(TraceBreakpointKind.SW_EXECUTE)) .filter(l -> l.getKinds().contains(TraceBreakpointKind.SW_EXECUTE))
.findAny() .findAny()
@ -1431,12 +1430,14 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
.findAny() .findAny()
.orElseThrow(); .orElseThrow();
CompletableFuture<Void> disable = lbEx.disable(); CompletableFuture<Void> disable = lbEx.disable();
handleToggleBreakpointInvocation(Unique.assertOne(lbEx.getTraceBreakpoints(trace)), false); handleToggleBreakpointInvocation(target1, Unique.assertOne(lbEx.getTraceBreakpoints(trace)),
false);
waitOn(disable); waitOn(disable);
waitForDomainObject(trace);
waitOn(breakpointService.changesSettled());
// TODO: This is more a test for the marker plugin, no? // TODO: This is more a test for the marker plugin, no?
waitForPass( assertEquals(State.MIXED, lbEx.computeState().sameAdddress(lbRw.computeState()));
() -> assertEquals(State.MIXED, lbEx.computeState().sameAdddress(lbRw.computeState())));
} }
protected void addTextMappingDead(Program p, ToyDBTraceBuilder tb) throws Throwable { protected void addTextMappingDead(Program p, ToyDBTraceBuilder tb) throws Throwable {
@ -1451,6 +1452,7 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
textRegion.getMinAddress()), textRegion.getMinAddress()),
new ProgramLocation(p, addr(p, 0x00400000)), 0x1000, false); new ProgramLocation(p, addr(p, 0x00400000)), 0x1000, false);
} }
waitForDomainObject(tb.trace);
} }
protected void addEnabledProgramBreakpointWithSleigh(Program p) { protected void addEnabledProgramBreakpointWithSleigh(Program p) {
@ -1459,6 +1461,7 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
.setBookmark(addr(p, 0x00400123), LogicalBreakpoint.ENABLED_BOOKMARK_TYPE, .setBookmark(addr(p, 0x00400123), LogicalBreakpoint.ENABLED_BOOKMARK_TYPE,
"SW_EXECUTE;1", "{sleigh: 'r0=0xbeef;'}"); "SW_EXECUTE;1", "{sleigh: 'r0=0xbeef;'}");
} }
waitForDomainObject(p);
} }
@Test @Test
@ -1471,16 +1474,18 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
programManager.openProgram(program); programManager.openProgram(program);
addTextMappingDead(program, tb); addTextMappingDead(program, tb);
waitForSwing();
addEnabledProgramBreakpointWithSleigh(program); addEnabledProgramBreakpointWithSleigh(program);
LogicalBreakpoint lb = waitForValue(() -> Unique.assertAtMostOne( waitOn(mappingService.changesSettled());
breakpointService.getBreakpointsAt(program, addr(program, 0x00400123)))); waitOn(breakpointService.changesSettled());
LogicalBreakpoint lb = Unique.assertAtMostOne(
breakpointService.getBreakpointsAt(program, addr(program, 0x00400123)));
assertEquals("r0=0xbeef;", lb.getEmuSleigh()); assertEquals("r0=0xbeef;", lb.getEmuSleigh());
waitOn(lb.enable()); waitOn(lb.enable());
waitForSwing(); waitForDomainObject(program);
waitOn(breakpointService.changesSettled());
TraceBreakpoint bpt = Unique.assertOne( TraceBreakpoint bpt = Unique.assertOne(
tb.trace.getBreakpointManager().getBreakpointsAt(0, tb.addr(0x55550123))); tb.trace.getBreakpointManager().getBreakpointsAt(0, tb.addr(0x55550123)));
@ -1497,21 +1502,22 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
programManager.openProgram(program); programManager.openProgram(program);
addEnabledProgramBreakpointWithSleigh(program); addEnabledProgramBreakpointWithSleigh(program);
LogicalBreakpoint lb = waitForValue(() -> Unique.assertAtMostOne( waitOn(breakpointService.changesSettled());
breakpointService.getBreakpointsAt(program, addr(program, 0x00400123)))); LogicalBreakpoint lb = Unique.assertAtMostOne(
breakpointService.getBreakpointsAt(program, addr(program, 0x00400123)));
assertEquals("r0=0xbeef;", lb.getEmuSleigh()); assertEquals("r0=0xbeef;", lb.getEmuSleigh());
addTextMappingDead(program, tb); addTextMappingDead(program, tb);
lb = waitForPass(() -> { waitOn(mappingService.changesSettled());
LogicalBreakpoint newLb = Unique.assertOne( waitOn(breakpointService.changesSettled());
breakpointService.getBreakpointsAt(program, addr(program, 0x00400123))); lb = Unique.assertOne(
assertTrue(newLb.getMappedTraces().contains(tb.trace)); breakpointService.getBreakpointsAt(program, addr(program, 0x00400123)));
return newLb; assertTrue(lb.getMappedTraces().contains(tb.trace));
});
waitOn(lb.enable()); waitOn(lb.enable());
waitForSwing(); waitForDomainObject(program);
waitOn(breakpointService.changesSettled());
TraceBreakpoint bpt = Unique.assertOne( TraceBreakpoint bpt = Unique.assertOne(
tb.trace.getBreakpointManager().getBreakpointsAt(0, tb.addr(0x55550123))); tb.trace.getBreakpointManager().getBreakpointsAt(0, tb.addr(0x55550123)));
@ -1541,21 +1547,25 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
false /* emuEnabled defaults to true */, ""); false /* emuEnabled defaults to true */, "");
bpt.setEmuSleigh("r0=0xbeef;"); bpt.setEmuSleigh("r0=0xbeef;");
} }
LogicalBreakpoint lb = waitForValue(() -> Unique.assertAtMostOne( waitForDomainObject(tb.trace);
breakpointService.getBreakpointsAt(tb.trace, tb.addr(0x55550123)))); waitOn(mappingService.changesSettled());
waitOn(breakpointService.changesSettled());
LogicalBreakpoint lb = Unique.assertAtMostOne(
breakpointService.getBreakpointsAt(tb.trace, tb.addr(0x55550123)));
assertEquals("r0=0xbeef;", lb.getEmuSleigh()); assertEquals("r0=0xbeef;", lb.getEmuSleigh());
addTextMappingDead(program, tb); addTextMappingDead(program, tb);
lb = waitForPass(() -> { waitOn(mappingService.changesSettled());
LogicalBreakpoint newLb = Unique.assertOne( waitOn(breakpointService.changesSettled());
breakpointService.getBreakpointsAt(program, addr(program, 0x00400123)));
assertTrue(newLb.getMappedTraces().contains(tb.trace)); lb = Unique
return newLb; .assertOne(breakpointService.getBreakpointsAt(program, addr(program, 0x00400123)));
}); assertTrue(lb.getMappedTraces().contains(tb.trace));
lb.enableForProgram(); lb.enableForProgram();
waitForSwing(); waitForDomainObject(tb.trace);
waitOn(breakpointService.changesSettled());
assertEquals("{\"sleigh\":\"r0\\u003d0xbeef;\"}", lb.getProgramBookmark().getComment()); assertEquals("{\"sleigh\":\"r0\\u003d0xbeef;\"}", lb.getProgramBookmark().getComment());
} }
@ -1566,6 +1576,8 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
* *
* <p> * <p>
* With the addition of the write-behind cache, this test is no longer sane. * With the addition of the write-behind cache, this test is no longer sane.
*
* @throws Throwable who doesn't?
*/ */
// @Test // @Test
public void testAbortAddBreakpointSetSleigh() throws Throwable { public void testAbortAddBreakpointSetSleigh() throws Throwable {
@ -1595,6 +1607,7 @@ public abstract class AbstractDebuggerLogicalBreakpointServiceTest<T, MR>
tid.abort(); tid.abort();
} }
waitForDomainObject(tb.trace); waitForDomainObject(tb.trace);
waitOn(breakpointService.changesSettled());
assertTrue(breakpointService.getAllBreakpoints().isEmpty()); assertTrue(breakpointService.getAllBreakpoints().isEmpty());
} }

View file

@ -17,6 +17,8 @@ package ghidra.app.plugin.core.debug.service.breakpoint;
import java.io.IOException; import java.io.IOException;
import org.junit.Ignore;
import db.Transaction; import db.Transaction;
import ghidra.dbg.target.schema.SchemaContext; import ghidra.dbg.target.schema.SchemaContext;
import ghidra.dbg.target.schema.TargetObjectSchema.SchemaName; import ghidra.dbg.target.schema.TargetObjectSchema.SchemaName;
@ -24,6 +26,7 @@ import ghidra.dbg.target.schema.XmlSchemaContext;
import ghidra.trace.model.Trace; import ghidra.trace.model.Trace;
import ghidra.trace.model.target.TraceObjectKeyPath; import ghidra.trace.model.target.TraceObjectKeyPath;
@Ignore("Deprecated")
public class DebuggerObjectRecorderLogicalBreakpointServiceTest public class DebuggerObjectRecorderLogicalBreakpointServiceTest
extends DebuggerRecorderLogicalBreakpointServiceTest { extends DebuggerRecorderLogicalBreakpointServiceTest {

View file

@ -19,9 +19,9 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import java.util.Set; import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.junit.Before; import org.junit.Before;
import org.junit.Ignore;
import db.Transaction; import db.Transaction;
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils; import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils;
@ -39,6 +39,7 @@ import ghidra.trace.model.breakpoint.TraceBreakpoint;
import ghidra.trace.model.memory.TraceMemoryRegion; import ghidra.trace.model.memory.TraceMemoryRegion;
import ghidra.trace.model.modules.TraceStaticMapping; import ghidra.trace.model.modules.TraceStaticMapping;
@Ignore("Deprecated")
public class DebuggerRecorderLogicalBreakpointServiceTest extends public class DebuggerRecorderLogicalBreakpointServiceTest extends
AbstractDebuggerLogicalBreakpointServiceTest<TraceRecorder, TestTargetMemoryRegion> { AbstractDebuggerLogicalBreakpointServiceTest<TraceRecorder, TestTargetMemoryRegion> {
@ -130,6 +131,7 @@ public class DebuggerRecorderLogicalBreakpointServiceTest extends
new ProgramLocation(p, addr(p, 0x00400000)), 0x1000, new ProgramLocation(p, addr(p, 0x00400000)), 0x1000,
false); false);
} }
waitForDomainObject(t);
} }
@Override @Override
@ -140,6 +142,7 @@ public class DebuggerRecorderLogicalBreakpointServiceTest extends
t.getStaticMappingManager().findContaining(addr(t, 0x55550000), r.getSnap()); t.getStaticMappingManager().findContaining(addr(t, 0x55550000), r.getSnap());
mapping.delete(); mapping.delete();
} }
waitForDomainObject(t);
} }
@Override @Override
@ -168,7 +171,7 @@ public class DebuggerRecorderLogicalBreakpointServiceTest extends
@Override @Override
protected void removeTargetSoftwareBreakpoint(TraceRecorder r) throws Throwable { protected void removeTargetSoftwareBreakpoint(TraceRecorder r) throws Throwable {
TargetBreakpointSpecContainer cont = getBreakpointContainer(r); TargetBreakpointSpecContainer cont = getBreakpointContainer(r);
cont.fetchElements().thenAccept(elements -> { waitOn(cont.fetchElements().thenCompose(elements -> {
for (TargetObject obj : elements.values()) { for (TargetObject obj : elements.values()) {
if (!(obj instanceof TargetBreakpointSpec) || if (!(obj instanceof TargetBreakpointSpec) ||
!(obj instanceof TargetDeletable)) { !(obj instanceof TargetDeletable)) {
@ -179,11 +182,12 @@ public class DebuggerRecorderLogicalBreakpointServiceTest extends
continue; continue;
} }
TargetDeletable del = (TargetDeletable) obj; TargetDeletable del = (TargetDeletable) obj;
del.delete(); return del.delete();
return;
} }
fail("No deletable software breakpoint spec found"); fail("No deletable software breakpoint spec found");
}).get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); throw new AssertionError();
}));
waitRecorder(r);
} }
@Override @Override
@ -200,14 +204,16 @@ public class DebuggerRecorderLogicalBreakpointServiceTest extends
} }
@Override @Override
protected void handleToggleBreakpointInvocation(TraceBreakpoint expectedBreakpoint, protected void handleToggleBreakpointInvocation(TraceRecorder target,
boolean expectedEnabled) throws Throwable { TraceBreakpoint expectedBreakpoint, boolean expectedEnabled) throws Throwable {
// Logic is in the Test model // Logic is in the Test model
waitRecorder(target);
} }
@Override @Override
protected void handleDeleteBreakpointInvocation(TraceBreakpoint expectedBreakpoint) protected void handleDeleteBreakpointInvocation(TraceRecorder target,
throws Throwable { TraceBreakpoint expectedBreakpoint) throws Throwable {
// Logic is in the Test model // Logic is in the Test model
waitRecorder(target);
} }
} }

View file

@ -53,6 +53,7 @@ public class DebuggerRmiLogicalBreakpointServiceTest extends
tb.trace.getObjectManager().createRootObject(SCHEMA_SESSION); tb.trace.getObjectManager().createRootObject(SCHEMA_SESSION);
tb.createObjectsProcessAndThreads(); tb.createObjectsProcessAndThreads();
} }
waitForDomainObject(tb.trace);
target1 = rmiCx.publishTarget(tool, tb.trace); target1 = rmiCx.publishTarget(tool, tb.trace);
} }
@ -64,6 +65,7 @@ public class DebuggerRmiLogicalBreakpointServiceTest extends
tb3.trace.getObjectManager().createRootObject(SCHEMA_SESSION); tb3.trace.getObjectManager().createRootObject(SCHEMA_SESSION);
tb3.createObjectsProcessAndThreads(); tb3.createObjectsProcessAndThreads();
} }
waitForDomainObject(tb3.trace);
target3 = rmiCx.publishTarget(tool, tb3.trace); target3 = rmiCx.publishTarget(tool, tb3.trace);
} }
@ -79,6 +81,7 @@ public class DebuggerRmiLogicalBreakpointServiceTest extends
try (Transaction tx = trace.openTransaction("Simulate step")) { try (Transaction tx = trace.openTransaction("Simulate step")) {
snapshot = trace.getTimeManager().createSnapshot("Simulated step"); snapshot = trace.getTimeManager().createSnapshot("Simulated step");
} }
waitForDomainObject(trace);
rmiCx.setLastSnapshot(trace, snapshot.getKey()); rmiCx.setLastSnapshot(trace, snapshot.getKey());
} }
@ -96,12 +99,15 @@ public class DebuggerRmiLogicalBreakpointServiceTest extends
protected TraceObjectMemoryRegion addTargetTextRegion(TraceRmiTarget target, long offset) protected TraceObjectMemoryRegion addTargetTextRegion(TraceRmiTarget target, long offset)
throws Throwable { throws Throwable {
Trace trace = target.getTrace(); Trace trace = target.getTrace();
TraceObjectMemoryRegion result;
try (Transaction tx = trace.openTransaction("Add .text")) { try (Transaction tx = trace.openTransaction("Add .text")) {
return Objects.requireNonNull( result = Objects.requireNonNull(
addMemoryRegion(trace.getObjectManager(), Lifespan.nowOn(target.getSnap()), addMemoryRegion(trace.getObjectManager(), Lifespan.nowOn(target.getSnap()),
tb.range(offset, offset + 0x0fff), "bin:.text", "rx") tb.range(offset, offset + 0x0fff), "bin:.text", "rx")
.queryInterface(TraceObjectMemoryRegion.class)); .queryInterface(TraceObjectMemoryRegion.class));
} }
waitForDomainObject(trace);
return result;
} }
@Override @Override
@ -113,12 +119,15 @@ public class DebuggerRmiLogicalBreakpointServiceTest extends
protected TraceObjectMemoryRegion addTargetDataRegion(TraceRmiTarget target) throws Throwable { protected TraceObjectMemoryRegion addTargetDataRegion(TraceRmiTarget target) throws Throwable {
Trace trace = target.getTrace(); Trace trace = target.getTrace();
long offset = 0x56550000; long offset = 0x56550000;
TraceObjectMemoryRegion result;
try (Transaction tx = trace.openTransaction("Add .data")) { try (Transaction tx = trace.openTransaction("Add .data")) {
return Objects.requireNonNull( result = Objects.requireNonNull(
addMemoryRegion(trace.getObjectManager(), Lifespan.nowOn(target.getSnap()), addMemoryRegion(trace.getObjectManager(), Lifespan.nowOn(target.getSnap()),
tb.range(offset, offset + 0x0fff), "bin:.data", "rw") tb.range(offset, offset + 0x0fff), "bin:.data", "rw")
.queryInterface(TraceObjectMemoryRegion.class)); .queryInterface(TraceObjectMemoryRegion.class));
} }
waitForDomainObject(trace);
return result;
} }
@Override @Override
@ -131,6 +140,7 @@ public class DebuggerRmiLogicalBreakpointServiceTest extends
new ProgramLocation(program, addr(program, 0x00400000)), 0x1000, new ProgramLocation(program, addr(program, 0x00400000)), 0x1000,
false); false);
} }
waitForDomainObject(trace);
} }
@Override @Override
@ -141,6 +151,7 @@ public class DebuggerRmiLogicalBreakpointServiceTest extends
t.getStaticMappingManager().findContaining(addr(t, 0x55550000), target.getSnap()); t.getStaticMappingManager().findContaining(addr(t, 0x55550000), target.getSnap());
mapping.delete(); mapping.delete();
} }
waitForDomainObject(t);
} }
@Override @Override
@ -188,6 +199,7 @@ public class DebuggerRmiLogicalBreakpointServiceTest extends
spec.getObject().remove(nowOn); spec.getObject().remove(nowOn);
} }
} }
waitForDomainObject(trace);
} }
@Override @Override
@ -204,8 +216,8 @@ public class DebuggerRmiLogicalBreakpointServiceTest extends
} }
@Override @Override
protected void handleToggleBreakpointInvocation(TraceBreakpoint expectedBreakpoint, protected void handleToggleBreakpointInvocation(TraceRmiTarget target,
boolean expectedEn) throws Throwable { TraceBreakpoint expectedBreakpoint, boolean expectedEn) throws Throwable {
if (!(expectedBreakpoint instanceof TraceObjectBreakpointLocation loc)) { if (!(expectedBreakpoint instanceof TraceObjectBreakpointLocation loc)) {
throw new AssertionError("Unexpected trace breakpoint type: " + expectedBreakpoint); throw new AssertionError("Unexpected trace breakpoint type: " + expectedBreakpoint);
} }
@ -213,6 +225,7 @@ public class DebuggerRmiLogicalBreakpointServiceTest extends
try (Transaction tx = tb.startTransaction()) { try (Transaction tx = tb.startTransaction()) {
loc.setEnabled(Lifespan.nowOn(0), expectedEn); loc.setEnabled(Lifespan.nowOn(0), expectedEn);
} }
waitForDomainObject(tb.trace);
rmiMethodToggleBreak.result(null); rmiMethodToggleBreak.result(null);
assertEquals(Map.ofEntries( assertEquals(Map.ofEntries(
Map.entry("breakpoint", loc.getSpecification().getObject()), Map.entry("breakpoint", loc.getSpecification().getObject()),
@ -220,8 +233,8 @@ public class DebuggerRmiLogicalBreakpointServiceTest extends
} }
@Override @Override
protected void handleDeleteBreakpointInvocation(TraceBreakpoint expectedBreakpoint) protected void handleDeleteBreakpointInvocation(TraceRmiTarget target,
throws Throwable { TraceBreakpoint expectedBreakpoint) throws Throwable {
if (!(expectedBreakpoint instanceof TraceObjectBreakpointLocation loc)) { if (!(expectedBreakpoint instanceof TraceObjectBreakpointLocation loc)) {
throw new AssertionError("Unexpected trace breakpoint type: " + expectedBreakpoint); throw new AssertionError("Unexpected trace breakpoint type: " + expectedBreakpoint);
} }
@ -229,6 +242,7 @@ public class DebuggerRmiLogicalBreakpointServiceTest extends
try (Transaction tx = tb.startTransaction()) { try (Transaction tx = tb.startTransaction()) {
loc.getObject().remove(Lifespan.nowOn(0)); loc.getObject().remove(Lifespan.nowOn(0));
} }
waitForDomainObject(tb.trace);
rmiMethodDeleteBreak.result(null); rmiMethodDeleteBreak.result(null);
assertEquals(Map.ofEntries( assertEquals(Map.ofEntries(
Map.entry("breakpoint", loc.getSpecification().getObject())), args); Map.entry("breakpoint", loc.getSpecification().getObject())), args);