mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 19:42:36 +02:00
GP-2752: Remove TargetObject.addListener() and related
This commit is contained in:
parent
c301dd2c89
commit
50c7217635
78 changed files with 384 additions and 1261 deletions
|
@ -53,7 +53,10 @@ public abstract class AbstractDebuggerWrappedConsoleConnection<T extends TargetO
|
|||
}
|
||||
|
||||
@Override
|
||||
public void consoleOutput(TargetObject console, Channel channel, byte[] out) {
|
||||
public void consoleOutput(TargetObject object, Channel channel, byte[] out) {
|
||||
if (object != targetConsole) {
|
||||
return;
|
||||
}
|
||||
OutputStream os;
|
||||
switch (channel) {
|
||||
case STDOUT:
|
||||
|
@ -79,6 +82,9 @@ public abstract class AbstractDebuggerWrappedConsoleConnection<T extends TargetO
|
|||
|
||||
@AttributeCallback(TargetObject.DISPLAY_ATTRIBUTE_NAME)
|
||||
public void displayChanged(TargetObject object, String display) {
|
||||
if (object != targetConsole) {
|
||||
return;
|
||||
}
|
||||
// TODO: Add setSubTitle(String) to InterpreterConsole
|
||||
if (guiConsole == null) {
|
||||
/**
|
||||
|
@ -92,7 +98,10 @@ public abstract class AbstractDebuggerWrappedConsoleConnection<T extends TargetO
|
|||
}
|
||||
|
||||
@AttributeCallback(TargetInterpreter.PROMPT_ATTRIBUTE_NAME)
|
||||
public void promptChanged(TargetObject interpreter, String prompt) {
|
||||
public void promptChanged(TargetObject object, String prompt) {
|
||||
if (object != targetConsole) {
|
||||
return;
|
||||
}
|
||||
if (guiConsole == null) {
|
||||
/**
|
||||
* Can happen during init. setPrompt will get called immediately after guiConsole is
|
||||
|
@ -105,10 +114,11 @@ public abstract class AbstractDebuggerWrappedConsoleConnection<T extends TargetO
|
|||
|
||||
@Override
|
||||
public void invalidated(TargetObject object, TargetObject branch, String reason) {
|
||||
if (object != targetConsole) {
|
||||
return;
|
||||
}
|
||||
Swing.runLater(() -> {
|
||||
if (object == targetConsole) { // Redundant
|
||||
consoleInvalidated();
|
||||
}
|
||||
consoleInvalidated();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -131,7 +141,7 @@ public abstract class AbstractDebuggerWrappedConsoleConnection<T extends TargetO
|
|||
T targetConsole) {
|
||||
this.plugin = plugin;
|
||||
this.targetConsole = targetConsole;
|
||||
targetConsole.addListener(listener);
|
||||
targetConsole.getModel().addModelListener(listener);
|
||||
}
|
||||
|
||||
protected abstract CompletableFuture<Void> sendLine(String line);
|
||||
|
@ -188,7 +198,7 @@ public abstract class AbstractDebuggerWrappedConsoleConnection<T extends TargetO
|
|||
.build();
|
||||
guiConsole.addAction(actionPin);
|
||||
|
||||
DockingAction interruptAction = InterpreterInterruptAction.builder(plugin)
|
||||
DockingAction interruptAction = InterpreterInterruptAction.builder(plugin)
|
||||
.onAction(this::sendInterrupt)
|
||||
.build();
|
||||
guiConsole.addAction(interruptAction);
|
||||
|
|
|
@ -20,7 +20,6 @@ import java.awt.Color;
|
|||
import java.awt.event.MouseEvent;
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
@ -795,9 +794,9 @@ public class DebuggerObjectsProvider extends ComponentProviderAdapter
|
|||
|
||||
@Override
|
||||
public void closeComponent() {
|
||||
TargetObject targetObject = getRoot().getTargetObject();
|
||||
if (targetObject != null) {
|
||||
targetObject.removeListener(getListener());
|
||||
DebuggerObjectModel model = getModel();
|
||||
if (model != null) {
|
||||
model.removeModelListener(getListener());
|
||||
}
|
||||
super.closeComponent();
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ import java.util.concurrent.CompletableFuture;
|
|||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import ghidra.async.AsyncUtils;
|
||||
import ghidra.dbg.DebuggerModelListener;
|
||||
import ghidra.dbg.DebuggerObjectModel;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
|
||||
|
@ -212,21 +211,12 @@ public class DummyTargetObject implements TargetObject {
|
|||
return attributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(DebuggerModelListener l) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener(DebuggerModelListener l) {
|
||||
}
|
||||
|
||||
public String getJoinedPath() {
|
||||
return joinedPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
// TODO Auto-generated method stub
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -235,6 +225,7 @@ public class DummyTargetObject implements TargetObject {
|
|||
return getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
return value;
|
||||
}
|
||||
|
|
|
@ -404,7 +404,6 @@ public class ObjectTree implements ObjectPane {
|
|||
DebuggerObjectsProvider provider = getProvider();
|
||||
ObjectContainer oc = node.getContainer();
|
||||
provider.deleteFromMap(oc);
|
||||
oc.getTargetObject().removeListener(provider.getListener());
|
||||
nodeMap.remove(path(node.getContainer()));
|
||||
}
|
||||
|
||||
|
|
|
@ -78,90 +78,40 @@ public class DebuggerModelServicePlugin extends Plugin
|
|||
// Since used for naming, no ':' allowed.
|
||||
public static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy.MM.dd-HH.mm.ss-z");
|
||||
|
||||
protected class ListenersForRemovalAndFocus {
|
||||
protected final DebuggerObjectModel model;
|
||||
protected TargetObject root;
|
||||
protected TargetFocusScope focusScope;
|
||||
protected class ForRemovalAndFocusListener extends AnnotatedDebuggerAttributeListener {
|
||||
public ForRemovalAndFocusListener() {
|
||||
super(MethodHandles.lookup());
|
||||
}
|
||||
|
||||
protected DebuggerModelListener forRemoval = new DebuggerModelListener() {
|
||||
@Override
|
||||
public void invalidated(TargetObject object, TargetObject branch, String reason) {
|
||||
synchronized (listenersByModel) {
|
||||
ListenersForRemovalAndFocus listener = listenersByModel.remove(model);
|
||||
if (listener == null) {
|
||||
return;
|
||||
}
|
||||
assert listener.forRemoval == this;
|
||||
dispose();
|
||||
}
|
||||
modelListeners.fire.elementRemoved(model);
|
||||
@Override
|
||||
public void invalidated(TargetObject object, TargetObject branch, String reason) {
|
||||
if (!object.isRoot()) {
|
||||
return;
|
||||
}
|
||||
DebuggerObjectModel model = object.getModel();
|
||||
synchronized (models) {
|
||||
models.remove(model);
|
||||
}
|
||||
model.removeModelListener(this);
|
||||
modelListeners.fire.elementRemoved(model);
|
||||
if (currentModel == model) {
|
||||
activateModel(null);
|
||||
}
|
||||
};
|
||||
|
||||
protected DebuggerModelListener forFocus =
|
||||
new AnnotatedDebuggerAttributeListener(MethodHandles.lookup()) {
|
||||
@AttributeCallback(TargetFocusScope.FOCUS_ATTRIBUTE_NAME)
|
||||
public void focusChanged(TargetObject object, TargetObject focused) {
|
||||
fireFocusEvent(focused);
|
||||
List<DebuggerModelServiceProxyPlugin> copy;
|
||||
synchronized (proxies) {
|
||||
copy = List.copyOf(proxies);
|
||||
}
|
||||
for (DebuggerModelServiceProxyPlugin proxy : copy) {
|
||||
proxy.fireFocusEvent(focused);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
protected ListenersForRemovalAndFocus(DebuggerObjectModel model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
protected CompletableFuture<Void> init() {
|
||||
return model.fetchModelRoot().thenCompose(r -> {
|
||||
synchronized (this) {
|
||||
this.root = r;
|
||||
}
|
||||
boolean isInvalid = false;
|
||||
try {
|
||||
r.addListener(this.forRemoval);
|
||||
}
|
||||
catch (IllegalStateException e) {
|
||||
isInvalid = true;
|
||||
}
|
||||
isInvalid |= !r.isValid();
|
||||
if (isInvalid) {
|
||||
forRemoval.invalidated(root, root, "Who knows?");
|
||||
}
|
||||
CompletableFuture<? extends TargetFocusScope> findSuitable =
|
||||
DebugModelConventions.findSuitable(TargetFocusScope.class, r);
|
||||
return findSuitable;
|
||||
}).thenAccept(fs -> {
|
||||
synchronized (this) {
|
||||
this.focusScope = fs;
|
||||
}
|
||||
if (fs != null) {
|
||||
fs.addListener(this.forFocus);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
TargetObject savedRoot;
|
||||
TargetFocusScope savedFocusScope;
|
||||
synchronized (this) {
|
||||
savedRoot = root;
|
||||
savedFocusScope = focusScope;
|
||||
@AttributeCallback(TargetFocusScope.FOCUS_ATTRIBUTE_NAME)
|
||||
public void focusChanged(TargetObject object, TargetObject focused) {
|
||||
// I don't think I care which scope
|
||||
fireFocusEvent(focused);
|
||||
List<DebuggerModelServiceProxyPlugin> copy;
|
||||
synchronized (proxies) {
|
||||
copy = List.copyOf(proxies);
|
||||
}
|
||||
if (savedRoot != null) {
|
||||
savedRoot.removeListener(this.forRemoval);
|
||||
}
|
||||
if (savedFocusScope != null) {
|
||||
savedFocusScope.removeListener(this.forFocus);
|
||||
for (DebuggerModelServiceProxyPlugin proxy : copy) {
|
||||
proxy.fireFocusEvent(focused);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
protected class ListenerOnRecorders implements TraceRecorderListener {
|
||||
@Override
|
||||
|
@ -195,8 +145,11 @@ public class DebuggerModelServicePlugin extends Plugin
|
|||
|
||||
protected final Set<DebuggerModelFactory> factories = new HashSet<>();
|
||||
// Keep strong references to my listeners, or they'll get torched
|
||||
protected final Map<DebuggerObjectModel, ListenersForRemovalAndFocus> listenersByModel =
|
||||
new LinkedHashMap<>();
|
||||
//protected final Map<DebuggerObjectModel, ListenersForRemovalAndFocus> listenersByModel =
|
||||
//new LinkedHashMap<>();
|
||||
protected final Set<DebuggerObjectModel> models = new LinkedHashSet<>();
|
||||
protected final ForRemovalAndFocusListener forRemovalAndFocusListener =
|
||||
new ForRemovalAndFocusListener();
|
||||
protected final Map<TargetObject, TraceRecorder> recordersByTarget = new WeakHashMap<>();
|
||||
|
||||
protected final ListenerSet<CollectionChangeListener<DebuggerModelFactory>> factoryListeners =
|
||||
|
@ -262,8 +215,8 @@ public class DebuggerModelServicePlugin extends Plugin
|
|||
|
||||
@Override
|
||||
public Set<DebuggerObjectModel> getModels() {
|
||||
synchronized (listenersByModel) {
|
||||
return Set.copyOf(listenersByModel.keySet());
|
||||
synchronized (models) {
|
||||
return Set.copyOf(models);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -286,20 +239,16 @@ public class DebuggerModelServicePlugin extends Plugin
|
|||
@Override
|
||||
public boolean addModel(DebuggerObjectModel model) {
|
||||
Objects.requireNonNull(model);
|
||||
synchronized (listenersByModel) {
|
||||
if (listenersByModel.containsKey(model)) {
|
||||
synchronized (models) {
|
||||
if (models.contains(model)) {
|
||||
return false;
|
||||
}
|
||||
ListenersForRemovalAndFocus listener = new ListenersForRemovalAndFocus(model);
|
||||
listener.init().exceptionally(e -> {
|
||||
Msg.error(this, "Could not add model " + model, e);
|
||||
synchronized (listenersByModel) {
|
||||
listenersByModel.remove(model);
|
||||
listener.dispose();
|
||||
}
|
||||
return null;
|
||||
});
|
||||
listenersByModel.put(model, listener);
|
||||
model.addModelListener(forRemovalAndFocusListener);
|
||||
TargetObject root = model.getModelRoot();
|
||||
if (!root.isValid()) {
|
||||
forRemovalAndFocusListener.invalidated(root, root,
|
||||
"Invalidated before or during add to service");
|
||||
}
|
||||
}
|
||||
modelListeners.fire.elementAdded(model);
|
||||
return true;
|
||||
|
@ -307,14 +256,12 @@ public class DebuggerModelServicePlugin extends Plugin
|
|||
|
||||
@Override
|
||||
public boolean removeModel(DebuggerObjectModel model) {
|
||||
ListenersForRemovalAndFocus listener;
|
||||
synchronized (listenersByModel) {
|
||||
listener = listenersByModel.remove(model);
|
||||
if (listener == null) {
|
||||
model.removeModelListener(forRemovalAndFocusListener);
|
||||
synchronized (models) {
|
||||
if (!models.remove(model)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
listener.dispose();
|
||||
modelListeners.fire.elementRemoved(model);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1,251 +0,0 @@
|
|||
/* ###
|
||||
* 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.model;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import ghidra.app.plugin.core.debug.mapping.DebuggerMemoryMapper;
|
||||
import ghidra.app.plugin.core.debug.service.model.interfaces.AbstractRecorderMemory;
|
||||
import ghidra.async.AsyncLazyMap;
|
||||
import ghidra.async.AsyncUtils;
|
||||
import ghidra.dbg.DebugModelConventions;
|
||||
import ghidra.dbg.DebugModelConventions.AllRequiredAccess;
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.TriConsumer;
|
||||
import ghidra.util.datastruct.ListenerSet;
|
||||
|
||||
public class RecorderComposedMemory implements AbstractRecorderMemory {
|
||||
|
||||
private static final int BLOCK_SIZE = 4096;
|
||||
private static final long BLOCK_MASK = -1L << 12;
|
||||
|
||||
protected final RecorderComposedMemory chain;
|
||||
|
||||
protected final NavigableMap<Address, TargetMemoryRegion> byMin = new TreeMap<>();
|
||||
|
||||
protected final Map<TargetMemoryRegion, TargetMemory> byRegion = new HashMap<>();
|
||||
protected final AsyncLazyMap<TargetMemory, AllRequiredAccess> accessibilityByMemory =
|
||||
new AsyncLazyMap<>(new HashMap<>(), this::fetchMemAccessibility) {
|
||||
public AllRequiredAccess remove(TargetMemory key) {
|
||||
AllRequiredAccess acc = super.remove(key);
|
||||
if (acc != null) {
|
||||
acc.removeChangeListener(getMemAccListeners().fire);
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
};
|
||||
|
||||
protected CompletableFuture<AllRequiredAccess> fetchMemAccessibility(TargetMemory mem) {
|
||||
return DebugModelConventions.trackAccessibility(mem).thenApply(acc -> {
|
||||
acc.addChangeListener(getMemAccListeners().fire);
|
||||
return acc;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get accessible memory, as viewed in the trace
|
||||
*
|
||||
* @param pred an additional predicate applied via "AND" with accessibility
|
||||
* @param memMapper target-to-trace mapping utility
|
||||
* @return the computed set
|
||||
*/
|
||||
@Override
|
||||
public AddressSet getAccessibleMemory(Predicate<TargetMemory> pred,
|
||||
DebuggerMemoryMapper memMapper) {
|
||||
synchronized (accessibilityByMemory) {
|
||||
// TODO: Might accomplish by using listeners and tracking the accessible set
|
||||
AddressSet accessible = new AddressSet();
|
||||
for (Entry<TargetMemoryRegion, TargetMemory> ent : byRegion.entrySet()) {
|
||||
TargetMemory mem = ent.getValue();
|
||||
if (!pred.test(mem)) {
|
||||
continue;
|
||||
}
|
||||
AllRequiredAccess acc = accessibilityByMemory.getCompletedMap().get(mem);
|
||||
if (acc == null || !acc.getAllAccessibility()) {
|
||||
continue;
|
||||
}
|
||||
AddressRange traceRange = memMapper.targetToTraceTruncated(ent.getKey().getRange());
|
||||
if (traceRange == null) {
|
||||
continue;
|
||||
}
|
||||
accessible.add(traceRange);
|
||||
}
|
||||
return accessible;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
private final ListenerSet<TriConsumer<Boolean, Boolean, Void>> memAccListeners =
|
||||
new ListenerSet(TriConsumer.class);
|
||||
|
||||
public RecorderComposedMemory(AbstractRecorderMemory memory) {
|
||||
this.chain = (RecorderComposedMemory) memory;
|
||||
}
|
||||
|
||||
protected TargetMemory getMemory(Address address, int length) {
|
||||
Entry<Address, TargetMemoryRegion> floor = findChainedFloor(address);
|
||||
if (floor == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"address " + address + " is not in any known region");
|
||||
}
|
||||
Address max;
|
||||
try {
|
||||
max = address.addNoWrap(length - 1);
|
||||
}
|
||||
catch (AddressOverflowException e) {
|
||||
throw new IllegalArgumentException("read extends beyond the address space");
|
||||
}
|
||||
if (!floor.getValue().getRange().contains(max)) {
|
||||
throw new IllegalArgumentException("read extends beyond a single region");
|
||||
}
|
||||
return byRegion.get(floor.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addMemory(TargetMemory memory) {
|
||||
// Do nothing
|
||||
// This delegates by region, so need regions even if I have memory
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRegion(TargetMemoryRegion region, TargetMemory memory) {
|
||||
synchronized (accessibilityByMemory) {
|
||||
TargetMemory old = byRegion.put(region, memory);
|
||||
assert old == null;
|
||||
byMin.put(region.getRange().getMinAddress(), region);
|
||||
accessibilityByMemory.get(memory).exceptionally(e -> {
|
||||
e = AsyncUtils.unwrapThrowable(e);
|
||||
Msg.error(this, "Could not track memory accessibility: " + e.getMessage());
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeMemory(TargetMemory invalid) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeRegion(TargetObject invalid) {
|
||||
if (!(invalid instanceof TargetMemoryRegion)) {
|
||||
return false;
|
||||
}
|
||||
synchronized (accessibilityByMemory) {
|
||||
TargetMemoryRegion invRegion = (TargetMemoryRegion) invalid;
|
||||
TargetMemory old = byRegion.remove(invRegion);
|
||||
assert old != null;
|
||||
byMin.remove(invRegion.getRange().getMinAddress());
|
||||
if (!old.isValid() || !byRegion.containsValue(old)) {
|
||||
accessibilityByMemory.remove(old);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
protected AllRequiredAccess findChainedMemoryAccess(TargetMemoryRegion region) {
|
||||
synchronized (accessibilityByMemory) {
|
||||
TargetMemory mem = byRegion.get(region);
|
||||
if (mem != null) {
|
||||
return accessibilityByMemory.getCompletedMap().get(mem);
|
||||
}
|
||||
return chain == null ? null : chain.findChainedMemoryAccess(region);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
public Entry<Address, TargetMemoryRegion> findChainedFloor(Address address) {
|
||||
synchronized (accessibilityByMemory) {
|
||||
Entry<Address, TargetMemoryRegion> myFloor = byMin.floorEntry(address);
|
||||
Entry<Address, TargetMemoryRegion> byChain =
|
||||
chain == null ? null : chain.findChainedFloor(address);
|
||||
if (byChain == null) {
|
||||
return myFloor;
|
||||
}
|
||||
if (myFloor == null) {
|
||||
return byChain;
|
||||
}
|
||||
int c = myFloor.getKey().compareTo(byChain.getKey());
|
||||
if (c < 0) {
|
||||
return byChain;
|
||||
}
|
||||
return myFloor;
|
||||
}
|
||||
}
|
||||
|
||||
protected AddressRange align(Address address, int length) {
|
||||
AddressSpace space = address.getAddressSpace();
|
||||
long offset = address.getOffset();
|
||||
Address start = space.getAddress(offset & BLOCK_MASK);
|
||||
Address end = space.getAddress(((offset + length - 1) & BLOCK_MASK) + BLOCK_SIZE - 1);
|
||||
return new AddressRangeImpl(start, end);
|
||||
}
|
||||
|
||||
protected AddressRange alignWithLimit(Address address, int length,
|
||||
TargetMemoryRegion limit) {
|
||||
return align(address, length).intersect(limit.getRange());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRange alignAndLimitToFloor(Address address, int length) {
|
||||
Entry<Address, TargetMemoryRegion> floor = findChainedFloor(address);
|
||||
if (floor == null) {
|
||||
return null;
|
||||
}
|
||||
return alignWithLimit(address, length, floor.getValue());
|
||||
}
|
||||
|
||||
public AddressRange alignWithOptionalLimit(Address address, int length,
|
||||
TargetMemoryRegion limit) {
|
||||
if (limit == null) {
|
||||
return alignAndLimitToFloor(address, length);
|
||||
}
|
||||
return alignWithLimit(address, length, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<byte[]> readMemory(Address address, int length) {
|
||||
synchronized (accessibilityByMemory) {
|
||||
TargetMemory mem = getMemory(address, length);
|
||||
if (mem != null) {
|
||||
return mem.readMemory(address, length);
|
||||
}
|
||||
return CompletableFuture.completedFuture(new byte[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> writeMemory(Address address, byte[] data) {
|
||||
synchronized (accessibilityByMemory) {
|
||||
TargetMemory mem = getMemory(address, data.length);
|
||||
if (mem != null) {
|
||||
return mem.writeMemory(address, data);
|
||||
}
|
||||
throw new IllegalArgumentException("read starts outside any address space");
|
||||
}
|
||||
}
|
||||
|
||||
public ListenerSet<TriConsumer<Boolean, Boolean, Void>> getMemAccListeners() {
|
||||
return memAccListeners;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,107 +0,0 @@
|
|||
/* ###
|
||||
* 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.model;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import ghidra.async.AsyncLazyMap;
|
||||
import ghidra.async.AsyncLazyMap.KeyedFuture;
|
||||
import ghidra.async.AsyncUtils;
|
||||
import ghidra.dbg.DebugModelConventions;
|
||||
import ghidra.dbg.DebugModelConventions.AllRequiredAccess;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.dbg.target.TargetRegisterBank;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.TriConsumer;
|
||||
|
||||
public class RecorderComposedRegisterSet {
|
||||
|
||||
private DefaultTraceRecorder recorder;
|
||||
|
||||
protected final TriConsumer<Boolean, Boolean, Void> listenerRegAccChanged =
|
||||
this::registerAccessibilityChanged;
|
||||
|
||||
protected void registerAccessibilityChanged(boolean old, boolean acc,
|
||||
Void __) {
|
||||
recorder.getListeners().fire.registerAccessibilityChanged(recorder);
|
||||
}
|
||||
|
||||
protected final AsyncLazyMap<TargetRegisterBank, AllRequiredAccess> accessibilityByRegBank =
|
||||
new AsyncLazyMap<>(new HashMap<>(), this::fetchRegAccessibility) {
|
||||
public AllRequiredAccess remove(TargetRegisterBank key) {
|
||||
AllRequiredAccess acc = super.remove(key);
|
||||
if (acc != null) {
|
||||
acc.removeChangeListener(listenerRegAccChanged);
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
};
|
||||
|
||||
protected CompletableFuture<AllRequiredAccess> fetchRegAccessibility(
|
||||
TargetRegisterBank bank) {
|
||||
return DebugModelConventions.trackAccessibility(bank).thenApply(acc -> {
|
||||
acc.addChangeListener(listenerRegAccChanged);
|
||||
return acc;
|
||||
});
|
||||
}
|
||||
|
||||
public RecorderComposedRegisterSet(DefaultTraceRecorder recorder) {
|
||||
this.recorder = recorder;
|
||||
}
|
||||
|
||||
public void updateRegisters(TargetRegisterBank newRegs, TargetRegisterBank oldRegs) {
|
||||
synchronized (accessibilityByRegBank) {
|
||||
if (oldRegs != null) {
|
||||
accessibilityByRegBank.remove(oldRegs);
|
||||
}
|
||||
accessibilityByRegBank.get(newRegs).exceptionally(e -> {
|
||||
e = AsyncUtils.unwrapThrowable(e);
|
||||
Msg.error(this, "Could not track register accessibility: " + e.getMessage());
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public boolean checkRegistersRemoved(Map<Integer, TargetRegisterBank> regs,
|
||||
TargetObject invalid) {
|
||||
synchronized (accessibilityByRegBank) {
|
||||
if (regs.values().remove(invalid)) {
|
||||
accessibilityByRegBank.remove((TargetRegisterBank) invalid);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isRegisterBankAccessible(TargetRegisterBank bank) {
|
||||
if (bank == null) {
|
||||
return false;
|
||||
}
|
||||
synchronized (accessibilityByRegBank) {
|
||||
KeyedFuture<?, AllRequiredAccess> future = accessibilityByRegBank.get(bank);
|
||||
if (future == null) {
|
||||
return false;
|
||||
}
|
||||
AllRequiredAccess acc = future.getNow(null);
|
||||
if (acc == null) {
|
||||
return false;
|
||||
}
|
||||
return acc.get();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue