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,8 +65,10 @@ class TraceBreakpointSet {
@Override
public String toString() {
synchronized (breakpoints) {
return String.format("<at %s in %s: %s>", address, trace.getName(), breakpoints);
}
}
/**
* Set the target when the trace is associated to a live target
@ -126,6 +128,7 @@ class TraceBreakpointSet {
*/
public TraceMode computeMode() {
TraceMode mode = TraceMode.NONE;
synchronized (breakpoints) {
if (getControlMode().useEmulatedBreakpoints()) {
for (IDHashed<TraceBreakpoint> bpt : breakpoints) {
mode = mode.combine(computeEmuMode(bpt.obj));
@ -143,6 +146,7 @@ class TraceBreakpointSet {
}
return mode;
}
}
/**
* Compute the mode (enablement) of the given breakpoint
@ -188,6 +192,7 @@ class TraceBreakpointSet {
*/
public String computeSleigh() {
String sleigh = null;
synchronized (breakpoints) {
for (IDHashed<TraceBreakpoint> bpt : breakpoints) {
String s = bpt.obj.getEmuSleigh();
if (sleigh != null && !sleigh.equals(s)) {
@ -197,6 +202,7 @@ class TraceBreakpointSet {
}
return sleigh;
}
}
/**
* Set the sleigh injection for all breakpoints in this set
@ -206,11 +212,13 @@ class TraceBreakpointSet {
public void setEmuSleigh(String emuSleigh) {
this.emuSleigh = emuSleigh;
try (Transaction tx = trace.openTransaction("Set breakpoint Sleigh")) {
synchronized (breakpoints) {
for (IDHashed<TraceBreakpoint> bpt : breakpoints) {
bpt.obj.setEmuSleigh(emuSleigh);
}
}
}
}
/**
* Check if this set actually contains any trace breakpoints
@ -218,8 +226,10 @@ class TraceBreakpointSet {
* @return true if empty, false otherwise
*/
public boolean isEmpty() {
synchronized (breakpoints) {
return breakpoints.isEmpty();
}
}
/**
* Get the breakpoints in this set
@ -227,8 +237,10 @@ class TraceBreakpointSet {
* @return the breakpoints
*/
public Set<TraceBreakpoint> getBreakpoints() {
synchronized (breakpoints) {
return breakpoints.stream().map(e -> e.obj).collect(Collectors.toUnmodifiableSet());
}
}
/**
* Add a breakpoint to this set
@ -246,8 +258,10 @@ class TraceBreakpointSet {
bpt.setEmuSleigh(emuSleigh);
}
}
synchronized (breakpoints) {
return breakpoints.add(new IDHashed<>(bpt));
}
}
/**
* Check if the given trace breakpoint "fits" in this set
@ -275,8 +289,10 @@ class TraceBreakpointSet {
* @return true if the set actually changes as a result
*/
public boolean remove(TraceBreakpoint bpt) {
synchronized (breakpoints) {
return breakpoints.remove(new IDHashed<>(bpt));
}
}
/**
* Plan to enable the logical breakpoint within this trace
@ -303,7 +319,7 @@ class TraceBreakpointSet {
public void planEnable(BreakpointActionSet actions, long length,
Collection<TraceBreakpointKind> kinds) {
long snap = getSnap();
if (breakpoints.isEmpty()) {
if (isEmpty()) {
if (target == null || getControlMode().useEmulatedBreakpoints()) {
planPlaceEmu(actions, snap, length, kinds);
}
@ -339,16 +355,20 @@ class TraceBreakpointSet {
}
private void planEnableTarget(BreakpointActionSet actions) {
synchronized (breakpoints) {
for (IDHashed<TraceBreakpoint> bpt : breakpoints) {
actions.planEnableTarget(target, bpt.obj);
}
}
}
private void planEnableEmu(BreakpointActionSet actions) {
synchronized (breakpoints) {
for (IDHashed<TraceBreakpoint> bpt : breakpoints) {
actions.planEnableEmu(bpt.obj);
}
}
}
/**
* Plan to disable the logical breakpoint in this trace
@ -369,16 +389,20 @@ class TraceBreakpointSet {
private void planDisableTarget(BreakpointActionSet actions, long length,
Collection<TraceBreakpointKind> kinds) {
synchronized (breakpoints) {
for (IDHashed<TraceBreakpoint> bpt : breakpoints) {
actions.planDisableTarget(target, bpt.obj);
}
}
}
private void planDisableEmu(BreakpointActionSet actions) {
synchronized (breakpoints) {
for (IDHashed<TraceBreakpoint> bpt : breakpoints) {
actions.planDisableEmu(bpt.obj);
}
}
}
/**
* Plan to delete the logical breakpoint in this trace
@ -399,14 +423,18 @@ class TraceBreakpointSet {
private void planDeleteTarget(BreakpointActionSet actions, long length,
Set<TraceBreakpointKind> kinds) {
synchronized (breakpoints) {
for (IDHashed<TraceBreakpoint> bpt : breakpoints) {
actions.planDeleteTarget(target, bpt.obj);
}
}
}
private void planDeleteEmu(BreakpointActionSet actions) {
synchronized (breakpoints) {
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;
import static ghidra.framework.main.DataTreeDialogType.*;
import static ghidra.framework.main.DataTreeDialogType.OPEN;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
@ -656,8 +656,10 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
@Override
public synchronized Collection<Trace> getOpenTraces() {
synchronized (listenersByTrace) {
return Set.copyOf(tracesView);
}
}
@Override
public DebuggerCoordinates getCurrent() {
@ -998,8 +1000,8 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
protected void doCloseTraces(Collection<Trace> traces, Collection<Target> targets) {
for (Trace t : traces) {
if (t.getConsumerList().contains(this)) {
firePluginEvent(new TraceClosedPluginEvent(getName(), t));
doTraceClosed(t);
firePluginEvent(new TraceClosedPluginEvent(getName(), t));
}
}
TargetActionTask.executeTask(tool, new DisconnectTask(tool, targets));

View file

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

View file

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

View file

@ -33,7 +33,7 @@ import ghidra.util.database.*;
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>
* Version history:

View file

@ -26,7 +26,30 @@ import ghidra.program.model.listing.CodeUnit;
import ghidra.trace.model.TraceAddressSnapRange;
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>> {
/**
* A means of obtaining the range attribute from each object
*
* @param <T> the type of objects returned by an iterator
*/
public interface Ranger<T> {
Address getMinAddress(T t);

View file

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

View file

@ -17,6 +17,8 @@ package ghidra.app.plugin.core.debug.service.breakpoint;
import java.io.IOException;
import org.junit.Ignore;
import db.Transaction;
import ghidra.dbg.target.schema.SchemaContext;
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.target.TraceObjectKeyPath;
@Ignore("Deprecated")
public class DebuggerObjectRecorderLogicalBreakpointServiceTest
extends DebuggerRecorderLogicalBreakpointServiceTest {

View file

@ -19,9 +19,9 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.junit.Before;
import org.junit.Ignore;
import db.Transaction;
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.modules.TraceStaticMapping;
@Ignore("Deprecated")
public class DebuggerRecorderLogicalBreakpointServiceTest extends
AbstractDebuggerLogicalBreakpointServiceTest<TraceRecorder, TestTargetMemoryRegion> {
@ -130,6 +131,7 @@ public class DebuggerRecorderLogicalBreakpointServiceTest extends
new ProgramLocation(p, addr(p, 0x00400000)), 0x1000,
false);
}
waitForDomainObject(t);
}
@Override
@ -140,6 +142,7 @@ public class DebuggerRecorderLogicalBreakpointServiceTest extends
t.getStaticMappingManager().findContaining(addr(t, 0x55550000), r.getSnap());
mapping.delete();
}
waitForDomainObject(t);
}
@Override
@ -168,7 +171,7 @@ public class DebuggerRecorderLogicalBreakpointServiceTest extends
@Override
protected void removeTargetSoftwareBreakpoint(TraceRecorder r) throws Throwable {
TargetBreakpointSpecContainer cont = getBreakpointContainer(r);
cont.fetchElements().thenAccept(elements -> {
waitOn(cont.fetchElements().thenCompose(elements -> {
for (TargetObject obj : elements.values()) {
if (!(obj instanceof TargetBreakpointSpec) ||
!(obj instanceof TargetDeletable)) {
@ -179,11 +182,12 @@ public class DebuggerRecorderLogicalBreakpointServiceTest extends
continue;
}
TargetDeletable del = (TargetDeletable) obj;
del.delete();
return;
return del.delete();
}
fail("No deletable software breakpoint spec found");
}).get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
throw new AssertionError();
}));
waitRecorder(r);
}
@Override
@ -200,14 +204,16 @@ public class DebuggerRecorderLogicalBreakpointServiceTest extends
}
@Override
protected void handleToggleBreakpointInvocation(TraceBreakpoint expectedBreakpoint,
boolean expectedEnabled) throws Throwable {
protected void handleToggleBreakpointInvocation(TraceRecorder target,
TraceBreakpoint expectedBreakpoint, boolean expectedEnabled) throws Throwable {
// Logic is in the Test model
waitRecorder(target);
}
@Override
protected void handleDeleteBreakpointInvocation(TraceBreakpoint expectedBreakpoint)
throws Throwable {
protected void handleDeleteBreakpointInvocation(TraceRecorder target,
TraceBreakpoint expectedBreakpoint) throws Throwable {
// 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.createObjectsProcessAndThreads();
}
waitForDomainObject(tb.trace);
target1 = rmiCx.publishTarget(tool, tb.trace);
}
@ -64,6 +65,7 @@ public class DebuggerRmiLogicalBreakpointServiceTest extends
tb3.trace.getObjectManager().createRootObject(SCHEMA_SESSION);
tb3.createObjectsProcessAndThreads();
}
waitForDomainObject(tb3.trace);
target3 = rmiCx.publishTarget(tool, tb3.trace);
}
@ -79,6 +81,7 @@ public class DebuggerRmiLogicalBreakpointServiceTest extends
try (Transaction tx = trace.openTransaction("Simulate step")) {
snapshot = trace.getTimeManager().createSnapshot("Simulated step");
}
waitForDomainObject(trace);
rmiCx.setLastSnapshot(trace, snapshot.getKey());
}
@ -96,12 +99,15 @@ public class DebuggerRmiLogicalBreakpointServiceTest extends
protected TraceObjectMemoryRegion addTargetTextRegion(TraceRmiTarget target, long offset)
throws Throwable {
Trace trace = target.getTrace();
TraceObjectMemoryRegion result;
try (Transaction tx = trace.openTransaction("Add .text")) {
return Objects.requireNonNull(
result = Objects.requireNonNull(
addMemoryRegion(trace.getObjectManager(), Lifespan.nowOn(target.getSnap()),
tb.range(offset, offset + 0x0fff), "bin:.text", "rx")
.queryInterface(TraceObjectMemoryRegion.class));
}
waitForDomainObject(trace);
return result;
}
@Override
@ -113,12 +119,15 @@ public class DebuggerRmiLogicalBreakpointServiceTest extends
protected TraceObjectMemoryRegion addTargetDataRegion(TraceRmiTarget target) throws Throwable {
Trace trace = target.getTrace();
long offset = 0x56550000;
TraceObjectMemoryRegion result;
try (Transaction tx = trace.openTransaction("Add .data")) {
return Objects.requireNonNull(
result = Objects.requireNonNull(
addMemoryRegion(trace.getObjectManager(), Lifespan.nowOn(target.getSnap()),
tb.range(offset, offset + 0x0fff), "bin:.data", "rw")
.queryInterface(TraceObjectMemoryRegion.class));
}
waitForDomainObject(trace);
return result;
}
@Override
@ -131,6 +140,7 @@ public class DebuggerRmiLogicalBreakpointServiceTest extends
new ProgramLocation(program, addr(program, 0x00400000)), 0x1000,
false);
}
waitForDomainObject(trace);
}
@Override
@ -141,6 +151,7 @@ public class DebuggerRmiLogicalBreakpointServiceTest extends
t.getStaticMappingManager().findContaining(addr(t, 0x55550000), target.getSnap());
mapping.delete();
}
waitForDomainObject(t);
}
@Override
@ -188,6 +199,7 @@ public class DebuggerRmiLogicalBreakpointServiceTest extends
spec.getObject().remove(nowOn);
}
}
waitForDomainObject(trace);
}
@Override
@ -204,8 +216,8 @@ public class DebuggerRmiLogicalBreakpointServiceTest extends
}
@Override
protected void handleToggleBreakpointInvocation(TraceBreakpoint expectedBreakpoint,
boolean expectedEn) throws Throwable {
protected void handleToggleBreakpointInvocation(TraceRmiTarget target,
TraceBreakpoint expectedBreakpoint, boolean expectedEn) throws Throwable {
if (!(expectedBreakpoint instanceof TraceObjectBreakpointLocation loc)) {
throw new AssertionError("Unexpected trace breakpoint type: " + expectedBreakpoint);
}
@ -213,6 +225,7 @@ public class DebuggerRmiLogicalBreakpointServiceTest extends
try (Transaction tx = tb.startTransaction()) {
loc.setEnabled(Lifespan.nowOn(0), expectedEn);
}
waitForDomainObject(tb.trace);
rmiMethodToggleBreak.result(null);
assertEquals(Map.ofEntries(
Map.entry("breakpoint", loc.getSpecification().getObject()),
@ -220,8 +233,8 @@ public class DebuggerRmiLogicalBreakpointServiceTest extends
}
@Override
protected void handleDeleteBreakpointInvocation(TraceBreakpoint expectedBreakpoint)
throws Throwable {
protected void handleDeleteBreakpointInvocation(TraceRmiTarget target,
TraceBreakpoint expectedBreakpoint) throws Throwable {
if (!(expectedBreakpoint instanceof TraceObjectBreakpointLocation loc)) {
throw new AssertionError("Unexpected trace breakpoint type: " + expectedBreakpoint);
}
@ -229,6 +242,7 @@ public class DebuggerRmiLogicalBreakpointServiceTest extends
try (Transaction tx = tb.startTransaction()) {
loc.getObject().remove(Lifespan.nowOn(0));
}
waitForDomainObject(tb.trace);
rmiMethodDeleteBreak.result(null);
assertEquals(Map.ofEntries(
Map.entry("breakpoint", loc.getSpecification().getObject())), args);