Merge remote-tracking branch 'origin/GP-2448_Dan_modelProviderActivateObject--SQUASHED'

This commit is contained in:
Ryan Kurtz
2022-08-19 11:06:20 -04:00
36 changed files with 1159 additions and 569 deletions
@@ -18,6 +18,7 @@ package ghidra.app.plugin.core.debug.disassemble;
import docking.ActionContext;
import docking.action.*;
import ghidra.app.context.ListingActionContext;
import ghidra.app.plugin.core.debug.DebuggerCoordinates;
import ghidra.app.plugin.core.debug.disassemble.DebuggerDisassemblerPlugin.Reqs;
import ghidra.app.plugin.core.debug.mapping.DebuggerPlatformMapper;
import ghidra.app.plugin.core.debug.mapping.DisassemblyResult;
@@ -28,7 +29,6 @@ import ghidra.program.util.ProgramSelection;
import ghidra.trace.model.Trace;
import ghidra.trace.model.program.TraceProgramView;
import ghidra.trace.model.target.TraceObject;
import ghidra.trace.model.thread.TraceObjectThread;
import ghidra.trace.model.thread.TraceThread;
import ghidra.util.HelpLocation;
import ghidra.util.task.TaskMonitor;
@@ -49,13 +49,6 @@ public class CurrentPlatformTraceDisassembleAction extends DockingAction {
setHelpLocation(new HelpLocation(plugin.getName(), "disassemble"));
}
protected TraceObject getObject(TraceThread thread) {
if (!(thread instanceof TraceObjectThread)) {
return null;
}
return ((TraceObjectThread) thread).getObject();
}
protected Reqs getReqs(ActionContext context) {
if (plugin.platformService == null) {
return null;
@@ -70,9 +63,10 @@ public class CurrentPlatformTraceDisassembleAction extends DockingAction {
}
TraceProgramView view = (TraceProgramView) program;
Trace trace = view.getTrace();
TraceThread thread = plugin.traceManager == null ? null
: plugin.traceManager.getCurrentThreadFor(trace);
TraceObject object = getObject(thread);
DebuggerCoordinates current = plugin.traceManager == null ? DebuggerCoordinates.NOWHERE
: plugin.traceManager.getCurrentFor(trace);
TraceThread thread = current.getThread();
TraceObject object = current.getObject();
DebuggerPlatformMapper mapper =
plugin.platformService.getMapper(trace, object, view.getSnap());
return new Reqs(mapper, thread, object, view);
@@ -497,7 +497,7 @@ public class DebuggerCopyIntoProgramDialog extends DialogComponentProvider {
if (recorder == null) {
return null;
}
if (!DebuggerCoordinates.view(source).withRecorder(recorder).isAliveAndReadsPresent()) {
if (!DebuggerCoordinates.NOWHERE.view(source).recorder(recorder).isAliveAndReadsPresent()) {
return null;
}
return recorder;
@@ -229,15 +229,14 @@ public class DebuggerTraceViewDiffPlugin extends AbstractDebuggerPlugin {
actionCompare.setSelected(true);
DebuggerCoordinates current = traceManager.getCurrent();
DebuggerCoordinates alternate =
traceManager.resolveCoordinates(DebuggerCoordinates.time(time));
DebuggerCoordinates alternate = traceManager.resolveTime(time);
PluginToolExecutorService toolExecutorService =
new PluginToolExecutorService(tool, "Computing diff", true, true, false, 500);
return traceManager.materialize(alternate).thenApplyAsync(snap -> {
clearMarkers();
TraceProgramView altView = alternate.getTrace().getFixedProgramView(snap);
altListingPanel.setProgram(altView);
trackingTrait.goToCoordinates(alternate.withView(altView));
trackingTrait.goToCoordinates(alternate.view(altView));
listingService.setListingPanel(altListingPanel);
return altView;
}, AsyncUtils.SWING_EXECUTOR).thenApplyAsync(altView -> {
@@ -408,7 +408,7 @@ public class DebuggerListingProvider extends CodeViewerProvider {
public void readDataState(SaveState saveState) {
if (!isMainListing()) {
DebuggerCoordinates coordinates =
DebuggerCoordinates.readDataState(tool, saveState, KEY_DEBUGGER_COORDINATES, true);
DebuggerCoordinates.readDataState(tool, saveState, KEY_DEBUGGER_COORDINATES);
coordinatesActivated(coordinates);
}
super.readDataState(saveState);
@@ -1041,7 +1041,8 @@ public class DebuggerListingProvider extends CodeViewerProvider {
return coordinates;
}
// Because the view's snap is changing with or without us.... So go with.
return current.withTime(coordinates.getTime());
// i.e., take the time, but not the thread
return current.time(coordinates.getTime());
}
public void goToCoordinates(DebuggerCoordinates coordinates) {
@@ -309,7 +309,8 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi
return coordinates;
}
// Because the view's snap is changing with or without us.... So go with.
return current.withTime(coordinates.getTime());
// i.e., take the time, but not the thread
return current.time(coordinates.getTime());
}
public void goToCoordinates(DebuggerCoordinates coordinates) {
@@ -439,7 +440,7 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi
protected void readDataState(SaveState saveState) {
if (!isMainViewer()) {
DebuggerCoordinates coordinates =
DebuggerCoordinates.readDataState(tool, saveState, KEY_DEBUGGER_COORDINATES, true);
DebuggerCoordinates.readDataState(tool, saveState, KEY_DEBUGGER_COORDINATES);
coordinatesActivated(coordinates);
}
super.readDataState(saveState);
@@ -199,7 +199,8 @@ public class DebuggerMemviewTraceListener extends TraceDomainObjectListener {
protected DebuggerCoordinates adjustCoordinates(DebuggerCoordinates coordinates) {
// Because the view's snap is changing with or without us.... So go with.
return current.withSnap(coordinates.getSnap());
// i.e., take the time, but not the thread
return current.time(coordinates.getTime());
}
public void coordinatesActivated(DebuggerCoordinates coordinates) {
@@ -28,6 +28,7 @@ import ghidra.trace.model.Trace;
import ghidra.trace.model.Trace.TraceObjectChangeType;
import ghidra.trace.model.Trace.TraceSnapshotChangeType;
import ghidra.trace.model.TraceDomainObjectListener;
import ghidra.trace.model.target.TraceObject;
import ghidra.trace.model.target.TraceObjectValue;
import ghidra.util.datastruct.Accumulator;
import ghidra.util.exception.CancelledException;
@@ -306,4 +307,6 @@ public abstract class AbstractQueryTableModel<T> extends ThreadedTableModel<T, T
public abstract void setDiffColor(Color diffColor);
public abstract void setDiffColorSel(Color diffColorSel);
public abstract T findTraceObject(TraceObject object);
}
@@ -29,6 +29,7 @@ import com.google.common.collect.Range;
import ghidra.app.plugin.core.debug.DebuggerCoordinates;
import ghidra.framework.plugintool.Plugin;
import ghidra.trace.model.target.TraceObject;
import ghidra.util.table.GhidraTable;
import ghidra.util.table.GhidraTableFilterPanel;
@@ -36,7 +37,7 @@ public abstract class AbstractQueryTablePanel<T> extends JPanel {
protected final AbstractQueryTableModel<T> tableModel;
protected final GhidraTable table;
private final GhidraTableFilterPanel<T> filterPanel;
protected final GhidraTableFilterPanel<T> filterPanel;
protected DebuggerCoordinates current = DebuggerCoordinates.NOWHERE;
protected boolean limitToSnap = false;
@@ -60,6 +61,10 @@ public abstract class AbstractQueryTablePanel<T> extends JPanel {
}
DebuggerCoordinates previous = current;
this.current = coords;
if (previous.getSnap() == current.getSnap() &&
previous.getTrace() == current.getTrace()) {
return;
}
tableModel.setDiffTrace(previous.getTrace());
tableModel.setTrace(current.getTrace());
tableModel.setDiffSnap(previous.getSnap());
@@ -158,6 +163,15 @@ public abstract class AbstractQueryTablePanel<T> extends JPanel {
filterPanel.setSelectedItem(item);
}
public boolean trySelect(TraceObject object) {
T t = tableModel.findTraceObject(object);
if (t == null) {
return false;
}
setSelectedItem(t);
return true;
}
public List<T> getSelectedItems() {
return filterPanel.getSelectedItems();
}
@@ -27,6 +27,7 @@ import javax.swing.*;
import docking.*;
import docking.action.DockingAction;
import docking.action.ToggleDockingAction;
import docking.widgets.tree.support.GTreeSelectionEvent.EventOrigin;
import ghidra.app.plugin.core.debug.DebuggerCoordinates;
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
@@ -156,7 +157,8 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable
@Override
public boolean verify(JComponent input) {
try {
setPath(TraceObjectKeyPath.parse(pathField.getText()), pathField);
TraceObjectKeyPath path = TraceObjectKeyPath.parse(pathField.getText());
setPath(path, pathField, EventOrigin.USER_GENERATED);
return true;
}
catch (IllegalArgumentException e) {
@@ -168,7 +170,8 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable
goButton = new JButton("Go");
ActionListener gotoPath = evt -> {
try {
setPath(TraceObjectKeyPath.parse(pathField.getText()), pathField);
TraceObjectKeyPath path = TraceObjectKeyPath.parse(pathField.getText());
setPath(path, pathField, EventOrigin.USER_GENERATED);
KeyboardFocusManager.getCurrentKeyboardFocusManager().clearGlobalFocusOwner();
}
catch (IllegalArgumentException e) {
@@ -245,15 +248,12 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable
return;
}
TraceObjectValue value = sel.get(0).getValue();
TraceObject parent = value.getParent();
TraceObjectKeyPath path;
if (parent == null) {
path = TraceObjectKeyPath.of();
}
else {
path = parent.getCanonicalPath().key(value.getEntryKey());
}
setPath(path, objectsTreePanel);
TraceObjectKeyPath path = value.getCanonicalPath();
// Prevent activation when selecting a link
EventOrigin origin =
value.isCanonical() ? evt.getEventOrigin() : EventOrigin.API_GENERATED;
setPath(path, objectsTreePanel, origin);
});
elementsTablePanel.addSelectionListener(evt -> {
if (evt.getValueIsAdjusting()) {
@@ -279,8 +279,11 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable
if (!value.isObject()) {
return;
}
attributesTablePanel
.setQuery(ModelQuery.attributesOf(value.getChild().getCanonicalPath()));
TraceObject object = value.getChild();
attributesTablePanel.setQuery(ModelQuery.attributesOf(object.getCanonicalPath()));
if (value.isCanonical()) {
activatePath(object.getCanonicalPath());
}
});
attributesTablePanel.addSelectionListener(evt -> {
if (evt.getValueIsAdjusting()) {
@@ -297,6 +300,15 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable
myActionContext = null;
}
contextChanged();
if (sel.size() != 1) {
return;
}
TraceObjectValue value = sel.get(0).getPath().getLastEntry();
// "canonical" implies "object"
if (value != null && value.isCanonical()) {
activatePath(value.getCanonicalPath());
}
});
elementsTablePanel.addMouseListener(new MouseAdapter() {
@@ -453,7 +465,7 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable
if (values.size() != 1) {
return;
}
setPath(values.get(0).getChild().getCanonicalPath(), null);
setPath(values.get(0).getChild().getCanonicalPath(), null, EventOrigin.USER_GENERATED);
}
private boolean isStepBackwardEnabled(ActionContext ignored) {
@@ -499,13 +511,79 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable
return mainPanel;
}
protected TraceObjectKeyPath findAsSibling(TraceObject object) {
Trace trace = current.getTrace();
if (trace == null) {
return null;
}
TraceObjectKeyPath parentPath = path.parent();
if (parentPath == null) {
return null;
}
TraceObject parent = trace.getObjectManager().getObjectByCanonicalPath(parentPath);
// TODO: Require parent to be a canonical container?
if (parent == null) {
return null;
}
for (TraceObjectValue value : parent.getValues()) {
if (Objects.equals(object, value.getValue())) {
return value.getCanonicalPath();
}
}
return null;
}
protected TraceObjectKeyPath findAsParent(TraceObject object) {
Trace trace = current.getTrace();
if (trace == null) {
return null;
}
TraceObjectManager objectManager = trace.getObjectManager();
if (objectManager.getRootObject() == null) {
return null;
}
TraceObjectValue sel = getTreeSelection();
if (sel == null) {
return null;
}
for (TraceObjectKeyPath p = sel.getCanonicalPath(); p != null; p = p.parent()) {
if (objectManager.getObjectByCanonicalPath(p) == object) {
return p;
}
}
return null;
}
public void coordinatesActivated(DebuggerCoordinates coords) {
this.current = coords;
objectsTreePanel.goToCoordinates(coords);
elementsTablePanel.goToCoordinates(coords);
attributesTablePanel.goToCoordinates(coords);
checkPath();
// NOTE: The plugin only calls this on the connected provider
// When cloning or restoring state, we MUST still consider the object
TraceObject object = coords.getObject();
if (object == null) {
checkPath();
return;
}
if (attributesTablePanel.trySelect(object)) {
return;
}
if (elementsTablePanel.trySelect(object)) {
return;
}
if (findAsParent(object) != null) {
checkPath();
return;
}
TraceObjectKeyPath sibling = findAsSibling(object);
if (sibling != null) {
setPath(sibling);
}
else {
setPath(object.getCanonicalPath());
}
}
public void traceClosed(Trace trace) {
@@ -514,8 +592,21 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable
}
}
protected void setPath(TraceObjectKeyPath path, JComponent source) {
if (Objects.equals(this.path, path)) {
protected void activatePath(TraceObjectKeyPath path) {
if (isClone) {
return;
}
Trace trace = current.getTrace();
if (trace != null) {
TraceObject object = trace.getObjectManager().getObjectByCanonicalPath(path);
if (object != null) {
traceManager.activateObject(object);
}
}
}
protected void setPath(TraceObjectKeyPath path, JComponent source, EventOrigin origin) {
if (Objects.equals(this.path, path) && getTreeSelection() != null) {
return;
}
this.path = path;
@@ -523,7 +614,10 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable
pathField.setText(path.toString());
}
if (source != objectsTreePanel) {
selectInTree(path);
setTreeSelection(path);
}
if (origin == EventOrigin.USER_GENERATED) {
activatePath(path);
}
elementsTablePanel.setQuery(ModelQuery.elementsOf(path));
attributesTablePanel.setQuery(ModelQuery.attributesOf(path));
@@ -538,7 +632,7 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable
}
public void setPath(TraceObjectKeyPath path) {
setPath(path, null);
setPath(path, null, EventOrigin.API_GENERATED);
}
public TraceObjectKeyPath getPath() {
@@ -639,8 +733,17 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable
attributesTablePanel.setDiffColorSel(diffColorSel);
}
protected void selectInTree(TraceObjectKeyPath path) {
objectsTreePanel.setSelectedKeyPaths(List.of(path));
protected void setTreeSelection(TraceObjectKeyPath path, EventOrigin origin) {
objectsTreePanel.setSelectedKeyPaths(List.of(path), origin);
}
protected void setTreeSelection(TraceObjectKeyPath path) {
setTreeSelection(path, EventOrigin.API_GENERATED);
}
protected TraceObjectValue getTreeSelection() {
AbstractNode sel = objectsTreePanel.getSelectedItem();
return sel == null ? null : sel.getValue();
}
@Override
@@ -671,7 +774,7 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable
public void readDataState(SaveState saveState) {
if (isClone) {
DebuggerCoordinates coords = DebuggerCoordinates.readDataState(plugin.getTool(),
saveState, KEY_DEBUGGER_COORDINATES, true);
saveState, KEY_DEBUGGER_COORDINATES);
if (coords != DebuggerCoordinates.NOWHERE) {
coordinatesActivated(coords);
}
@@ -394,6 +394,16 @@ public class ObjectTableModel extends AbstractQueryTableModel<ValueRow> {
return descriptor;
}
@Override
public ValueRow findTraceObject(TraceObject object) {
for (ValueRow row : getModelData()) {
if (row.getValue().getValue() == object && row.getValue().isCanonical()) {
return row;
}
}
return null;
}
@Override
public void setDiffColor(Color diffColor) {
valueColumn.setDiffColor(diffColor);
@@ -160,10 +160,12 @@ public class ObjectTreeModel implements DisplaysModified {
protected AbstractNode getOrCreateNode(TraceObjectValue value) {
if (value.getParent() == null) {
root.unloadChildren();
return root;
}
AbstractNode node =
byValue.computeIfAbsent(new IDKeyed<>(value), k -> createNode(value));
node.unloadChildren();
//AbstractNode node = createNode(value);
if (value.isCanonical()) {
byObject.put(new IDKeyed<>(value.getChild()), node);
@@ -303,6 +305,13 @@ public class ObjectTreeModel implements DisplaysModified {
@Override
protected void childCreated(TraceObjectValue value) {
if (!isValueVisible(value)) {
return;
}
if (nodeCache.getByValue(value) != null) {
super.childCreated(value);
return;
}
try (KeepTreeState keep = KeepTreeState.ifNotNull(getTree())) {
unloadChildren();
}
@@ -28,8 +28,8 @@ import javax.swing.tree.TreePath;
import com.google.common.collect.Range;
import docking.widgets.tree.GTree;
import docking.widgets.tree.GTreeNode;
import docking.widgets.tree.support.GTreeRenderer;
import docking.widgets.tree.support.GTreeSelectionEvent.EventOrigin;
import docking.widgets.tree.support.GTreeSelectionListener;
import ghidra.app.plugin.core.debug.DebuggerCoordinates;
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
@@ -115,6 +115,10 @@ public class ObjectsTreePanel extends JPanel {
}
DebuggerCoordinates previous = current;
this.current = coords;
if (previous.getSnap() == current.getSnap() &&
previous.getTrace() == current.getTrace()) {
return;
}
try (KeepTreeState keep = keepTreeState()) {
treeModel.setDiffTrace(computeDiffTrace(current.getTrace(), previous.getTrace()));
treeModel.setTrace(current.getTrace());
@@ -255,14 +259,18 @@ public class ObjectsTreePanel extends JPanel {
return treeModel.getNode(path);
}
public void setSelectedKeyPaths(Collection<TraceObjectKeyPath> keyPaths) {
List<GTreeNode> nodes = new ArrayList<>();
public void setSelectedKeyPaths(Collection<TraceObjectKeyPath> keyPaths, EventOrigin origin) {
List<TreePath> treePaths = new ArrayList<>();
for (TraceObjectKeyPath path : keyPaths) {
AbstractNode node = getNode(path);
if (node != null) {
nodes.add(node);
treePaths.add(node.getTreePath());
}
}
tree.setSelectedNodes(nodes);
tree.setSelectionPaths(treePaths.toArray(TreePath[]::new), origin);
}
public void setSelectedKeyPaths(Collection<TraceObjectKeyPath> keyPaths) {
setSelectedKeyPaths(keyPaths, EventOrigin.API_GENERATED);
}
}
@@ -26,7 +26,7 @@ import ghidra.app.plugin.core.debug.gui.model.PathTableModel.PathRow;
import ghidra.app.plugin.core.debug.gui.model.columns.*;
import ghidra.framework.plugintool.Plugin;
import ghidra.trace.model.Trace;
import ghidra.trace.model.target.TraceObjectValPath;
import ghidra.trace.model.target.*;
public class PathTableModel extends AbstractQueryTableModel<PathRow> {
/** Initialized in {@link #createTableColumnDescriptor()}, which precedes this. */
@@ -89,6 +89,12 @@ public class PathTableModel extends AbstractQueryTableModel<PathRow> {
public boolean isModified() {
return isValueModified(path.getLastEntry());
}
public boolean isLastCanonical() {
TraceObjectValue last = path.getLastEntry();
// Root is canonical
return last == null || last.isCanonical();
}
}
public PathTableModel(Plugin plugin) {
@@ -143,6 +149,16 @@ public class PathTableModel extends AbstractQueryTableModel<PathRow> {
return descriptor;
}
@Override
public PathRow findTraceObject(TraceObject object) {
for (PathRow row : getModelData()) {
if (row.getValue() == object && row.isLastCanonical()) {
return row;
}
}
return null;
}
@Override
public void setDiffColor(Color diffColor) {
valueColumn.setDiffColor(diffColor);
@@ -279,7 +279,7 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
}
private void objectRestored(DomainObjectChangeRecord rec) {
coordinatesActivated(current.withReFoundThread());
coordinatesActivated(current.reFindThread());
}
private void registerValueChanged(TraceAddressSpace space, TraceAddressSnapRange range,
@@ -1384,7 +1384,7 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
public void readDataState(SaveState saveState) {
if (isClone) {
coordinatesActivated(
DebuggerCoordinates.readDataState(tool, saveState, KEY_DEBUGGER_COORDINATES, true));
DebuggerCoordinates.readDataState(tool, saveState, KEY_DEBUGGER_COORDINATES));
}
}
}
@@ -117,7 +117,7 @@ public class DebuggerTimeProvider extends ComponentProviderAdapter {
myActionContext = null;
return;
}
if (snap.longValue() == current.getSnap().longValue()) {
if (snap.longValue() == current.getSnap()) {
return;
}
myActionContext = new DebuggerSnapActionContext(current.getTrace(), snap);
@@ -35,8 +35,7 @@ import ghidra.program.model.mem.*;
import ghidra.trace.model.Trace;
import ghidra.trace.model.Trace.TraceProgramViewListener;
import ghidra.trace.model.memory.TraceMemoryOperations;
import ghidra.trace.model.program.TraceProgramView;
import ghidra.trace.model.program.TraceProgramViewMemory;
import ghidra.trace.model.program.*;
import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.model.time.schedule.PatchStep;
import ghidra.trace.model.time.schedule.TraceSchedule;
@@ -121,7 +120,8 @@ public class DebuggerStateEditingServicePlugin extends AbstractDebuggerPlugin
DebuggerCoordinates coordinates = getCoordinates();
Trace trace = coordinates.getTrace();
switch (getCurrentMode(trace)) {
StateEditingMode mode = getCurrentMode(trace);
switch (mode) {
case READ_ONLY:
return CompletableFuture
.failedFuture(new MemoryAccessException("Read-only mode"));
@@ -178,6 +178,9 @@ public class DebuggerStateEditingServicePlugin extends AbstractDebuggerPlugin
protected CompletableFuture<Void> writeEmulatorVariable(DebuggerCoordinates coordinates,
Address address, byte[] data) {
if (!(coordinates.getView() instanceof TraceVariableSnapProgramView)) {
throw new IllegalArgumentException("Cannot emulate using a Fixed Program View");
}
TraceThread thread = coordinates.getThread();
if (thread == null) {
// TODO: Well, technically, only for register edits
@@ -187,7 +190,7 @@ public class DebuggerStateEditingServicePlugin extends AbstractDebuggerPlugin
.patched(thread, PatchStep.generateSleigh(
coordinates.getTrace().getBaseLanguage(), address, data));
DebuggerCoordinates withTime = coordinates.withTime(time);
DebuggerCoordinates withTime = coordinates.time(time);
Long found = traceManager.findSnapshot(withTime);
// Materialize it on the same thread (even if swing)
// It shouldn't take long, since we're only appending one step.
@@ -237,17 +240,11 @@ public class DebuggerStateEditingServicePlugin extends AbstractDebuggerPlugin
@Override
public DebuggerCoordinates getCoordinates() {
DebuggerCoordinates current = traceManager.getCurrentFor(trace);
if (current != null) {
return current;
if (!traceManager.getOpenTraces().contains(trace)) {
throw new IllegalStateException(
"Trace " + trace + " is not opened in the trace manager.");
}
DebuggerCoordinates resolved =
traceManager.resolveCoordinates(DebuggerCoordinates.trace(trace));
if (resolved != null) {
return resolved;
}
throw new IllegalStateException(
"Trace " + trace + " is not opened in the trace manager.");
return traceManager.resolveTrace(trace);
}
}
@@ -266,7 +263,7 @@ public class DebuggerStateEditingServicePlugin extends AbstractDebuggerPlugin
@Override
public DebuggerCoordinates getCoordinates() {
return traceManager.resolveCoordinates(DebuggerCoordinates.view(view));
return traceManager.resolveView(view);
}
@Override
@@ -268,6 +268,9 @@ class ObjectRecorder {
protected <T extends TargetObject> T getTargetFrameInterface(TraceThread thread, int frameLevel,
Class<T> targetObjectIf) {
if (thread == null) {
return null;
}
TraceObject object = ((TraceObjectThread) thread).getObject();
PathMatcher matcher = object.getTargetSchema().searchFor(targetObjectIf, false);
PathPattern pattern = matcher.getSingletonPattern();
@@ -53,7 +53,6 @@ import ghidra.trace.model.program.TraceVariableSnapProgramView;
import ghidra.trace.model.stack.TraceStackFrame;
import ghidra.trace.model.target.TraceObject;
import ghidra.trace.model.target.TraceObjectKeyPath;
import ghidra.trace.model.thread.TraceObjectThread;
import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.model.time.TraceSnapshot;
import ghidra.trace.model.time.schedule.TraceSchedule;
@@ -105,7 +104,7 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
if (supportsFocus(recorder)) {
// TODO: Same for stack frame? I can't imagine it's as common as this....
if (thread == recorder.getTraceThreadForSuccessor(recorder.getFocus())) {
activate(DebuggerCoordinates.thread(thread));
activate(current.thread(thread));
}
return;
}
@@ -115,7 +114,7 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
if (current.getThread() != null) {
return;
}
activate(DebuggerCoordinates.thread(thread));
activate(current.thread(thread));
}
private void threadDeleted(TraceThread thread) {
@@ -124,7 +123,7 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
lastCoordsByTrace.remove(trace);
}
if (current.getThread() == thread) {
activate(DebuggerCoordinates.trace(trace));
activate(current.thread(null));
}
}
}
@@ -422,127 +421,24 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
return recorder != null && recorder.isSupportsFocus();
}
@Override
public DebuggerCoordinates resolveCoordinates(DebuggerCoordinates coordinates) {
if (coordinates == DebuggerCoordinates.NOWHERE) {
return DebuggerCoordinates.NOWHERE;
}
Trace trace = coordinates.getTrace();
if (trace == null) {
trace = current.getTrace();
}
protected DebuggerCoordinates fillInRecorder(Trace trace, DebuggerCoordinates coordinates) {
if (trace == null) {
return DebuggerCoordinates.NOWHERE;
}
DebuggerCoordinates lastForTrace = lastCoordsByTrace.get(trace);
// Note: override recorder with that known to service
if (coordinates.getRecorder() != null) {
return coordinates;
}
TraceRecorder recorder = computeRecorder(trace);
TargetObject focus = recorder == null ? null : recorder.getFocus();
TraceThread thread = coordinates.getThread();
if (thread == null) {
if (supportsFocus(recorder)) {
thread = threadFromTargetFocus(recorder, focus);
}
if (thread /*still*/ == null) { // either no focus support, or focus is not a thread
thread = lastForTrace == null ? null : lastForTrace.getThread();
}
// NOTE, if still null without focus support,
// we will take the eldest live thread at the resolved snap
if (recorder == null) {
return coordinates;
}
TraceObject object = coordinates.getObject();
if (object == null) {
if (supportsFocus(recorder)) {
object = objectFromTargetFocus(recorder, focus);
}
if (object /*still*/ == null) { // either no focus support, or focus is not recorded
object = lastForTrace == null ? null : lastForTrace.getObject();
if (object != null) {
TraceObjectThread objThread =
object.queryCanonicalAncestorsInterface(TraceObjectThread.class)
.findFirst()
.orElse(null);
if (objThread != thread) {
object = null; // Abandon remembered object
}
}
}
if (object /*still*/ == null && thread instanceof TraceObjectThread objThread) {
// TODO: Seek the frame out?
object = objThread.getObject();
}
if (object /*still*/ == null) {
object = trace.getObjectManager().getRootObject();
// Could still be null, but that means no objects in the trace at all
}
}
/**
* Only select a default thread if the trace is not live. If it is live, and the model
* supports focus, then we should expect the debugger to control thread/frame focus.
*
* Note: If recorder has current thread, it should already be in the threadFocusByTrace map
*/
// Note: override view. May not agree on snap now, but will upon activation
TraceProgramView view = trace.getProgramView();
TraceSchedule time = coordinates.getTime();
if (time == null) {
if (recorder != null && autoActivatePresent.get() && trace != current.getTrace()) {
time = TraceSchedule.snap(recorder.getSnap());
}
else {
time = lastForTrace == null ? TraceSchedule.snap(0) : lastForTrace.getTime();
}
}
if (!supportsFocus(recorder)) {
if (thread /*still*/ == null) {
Iterator<? extends TraceThread> it =
trace.getThreadManager().getLiveThreads(time.getSnap()).iterator();
// docs say eldest come first
if (it.hasNext()) {
thread = it.next();
}
}
if (thread /*STILL!?*/ == null) {
Iterator<? extends TraceThread> it =
trace.getThreadManager().getAllThreads().iterator();
if (it.hasNext()) {
thread = it.next();
}
}
}
Integer frame = coordinates.getFrame();
if (frame == null) {
if (supportsFocus(recorder)) {
TraceStackFrame traceFrame = frameFromTargetFocus(recorder, focus);
if (traceFrame == null) {
Msg.warn(this,
"Focus-capable model has not reported frame focus. Imposing a default");
}
else {
frame = traceFrame.getLevel();
}
}
if (frame /*still*/ == null && lastForTrace != null &&
thread == lastForTrace.getThread()) {
// TODO: Memorize frame by thread, instead of by trace?
frame = lastForTrace.getFrame();
}
}
// TODO: Is it reasonable to change back to frame 0 on snap change?
// Only 0 (possibly synthetic) is guaranteed to exist in any snap
if (frame == null || !time.isSnapOnly() ||
!Objects.equals(time.getSnap(), current.getSnap())) {
frame = 0;
}
return DebuggerCoordinates.all(trace, recorder, thread, view, Objects.requireNonNull(time),
Objects.requireNonNull(frame), object);
return coordinates.recorder(recorder);
}
protected DebuggerCoordinates doSetCurrent(DebuggerCoordinates newCurrent) {
newCurrent = newCurrent == null ? DebuggerCoordinates.NOWHERE : newCurrent;
synchronized (listenersByTrace) {
DebuggerCoordinates resolved = resolveCoordinates(newCurrent);
DebuggerCoordinates resolved = fillInRecorder(newCurrent.getTrace(), newCurrent);
if (current.equals(resolved)) {
return null;
}
@@ -592,13 +488,7 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
return false;
}
}
TraceThread thread = threadFromTargetFocus(recorder, obj);
TraceObject object = objectFromTargetFocus(recorder, obj);
long snap = recorder.getSnap();
TraceStackFrame traceFrame = frameFromTargetFocus(recorder, obj);
Integer frame = traceFrame == null ? null : traceFrame.getLevel();
activateNoFocus(DebuggerCoordinates.all(trace, recorder, thread, null,
TraceSchedule.snap(snap), frame, object));
activateNoFocus(getCurrentFor(trace).object(obj));
return true;
}
@@ -626,11 +516,15 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
protected void updateCurrentRecorder() {
TraceRecorder recorder = computeRecorder(current.getTrace());
if (recorder == null) {
return;
}
DebuggerCoordinates toActivate = current.recorder(recorder);
if (autoActivatePresent.get()) {
activate(DebuggerCoordinates.recorder(recorder));
activate(toActivate.snap(recorder.getSnap()));
}
else {
activate(current.withRecorder(recorder));
activate(toActivate);
}
}
@@ -670,7 +564,11 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
@Override
public DebuggerCoordinates getCurrentFor(Trace trace) {
return lastCoordsByTrace.get(trace);
synchronized (listenersByTrace) {
// If known, fill in recorder ASAP, so it determines the time
return fillInRecorder(trace,
lastCoordsByTrace.getOrDefault(trace, DebuggerCoordinates.NOWHERE));
}
}
@Override
@@ -688,12 +586,6 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
return current.getThread();
}
@Override
public TraceThread getCurrentThreadFor(Trace trace) {
DebuggerCoordinates coords = lastCoordsByTrace.get(trace);
return coords == null ? null : coords.getThread();
}
@Override
public long getCurrentSnap() {
return current.getSnap();
@@ -709,6 +601,7 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
return current.getObject();
}
@Override
public Long findSnapshot(DebuggerCoordinates coordinates) {
if (coordinates.getTime().isSnapOnly()) {
return coordinates.getSnap();
@@ -1021,34 +914,47 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
prepareViewAndFireEvent(resolved);
}
protected static boolean isSameFocus(DebuggerCoordinates prev, DebuggerCoordinates resolved) {
if (!Objects.equals(prev.getObject(), resolved.getObject())) {
return false;
}
if (!Objects.equals(prev.getFrame(), resolved.getFrame())) {
return false;
}
if (!Objects.equals(prev.getThread(), resolved.getThread())) {
return false;
}
if (!Objects.equals(prev.getTrace(), resolved.getTrace())) {
return false;
}
return true;
}
protected static TargetObject translateToFocus(DebuggerCoordinates prev,
DebuggerCoordinates resolved) {
if (!resolved.isAliveAndPresent()) {
return null;
}
if (isSameFocus(prev, resolved)) {
return null;
}
TraceRecorder recorder = resolved.getRecorder();
if (!Objects.equals(prev.getObject(), resolved.getObject())) {
TraceObject obj = resolved.getObject();
if (obj != null) {
TargetObject object =
recorder.getTarget().getSuccessor(obj.getCanonicalPath().getKeyList());
if (object != null) {
return object;
}
TraceObject obj = resolved.getObject();
if (obj != null) {
TargetObject object =
recorder.getTarget().getSuccessor(obj.getCanonicalPath().getKeyList());
if (object != null) {
return object;
}
}
if (!Objects.equals(prev.getFrame(), resolved.getFrame())) {
TargetStackFrame frame =
recorder.getTargetStackFrame(resolved.getThread(), resolved.getFrame());
if (frame != null) {
return frame;
}
TargetStackFrame frame =
recorder.getTargetStackFrame(resolved.getThread(), resolved.getFrame());
if (frame != null) {
return frame;
}
if (!Objects.equals(prev.getThread(), resolved.getThread())) {
TargetThread thread = recorder.getTargetThread(resolved.getThread());
if (thread != null) {
return thread;
}
TargetThread thread = recorder.getTargetThread(resolved.getThread());
if (thread != null) {
return thread;
}
return recorder.getTarget();
}
@@ -1094,33 +1000,40 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
}
@Override
public void activateTrace(Trace trace) {
activate(DebuggerCoordinates.trace(trace));
public DebuggerCoordinates resolveTrace(Trace trace) {
return getCurrentFor(trace).trace(trace);
}
@Override
public void activateThread(TraceThread thread) {
activate(DebuggerCoordinates.thread(thread));
public DebuggerCoordinates resolveThread(TraceThread thread) {
Trace trace = thread == null ? null : thread.getTrace();
return getCurrentFor(trace).thread(thread);
}
@Override
public void activateSnap(long snap) {
activateNoFocusChange(DebuggerCoordinates.snap(snap));
public DebuggerCoordinates resolveSnap(long snap) {
return current.snap(snap);
}
@Override
public void activateTime(TraceSchedule time) {
activate(DebuggerCoordinates.time(time));
public DebuggerCoordinates resolveTime(TraceSchedule time) {
return current.time(time);
}
@Override
public void activateFrame(int frameLevel) {
activate(DebuggerCoordinates.frame(frameLevel));
public DebuggerCoordinates resolveView(TraceProgramView view) {
Trace trace = view == null ? null : view.getTrace();
return getCurrentFor(trace).view(view);
}
@Override
public void activateObject(TraceObject object) {
activate(DebuggerCoordinates.object(object));
public DebuggerCoordinates resolveFrame(int frameLevel) {
return current.frame(frameLevel);
}
@Override
public DebuggerCoordinates resolveObject(TraceObject object) {
return current.object(object);
}
@Override
@@ -1135,7 +1048,7 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
}
}*/
if (curRecorder != null) {
activateNoFocus(DebuggerCoordinates.snap(curRecorder.getSnap()));
activateNoFocus(current.snap(curRecorder.getSnap()));
}
}
}
@@ -1274,12 +1187,12 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
String stateName = PREFIX_OPEN_TRACE + index;
// Trace will be opened by readDataState, resolve causes update to focus and view
DebuggerCoordinates coords =
DebuggerCoordinates.readDataState(tool, saveState, stateName, true);
DebuggerCoordinates.readDataState(tool, saveState, stateName);
if (coords.getTrace() != null) {
lastCoordsByTrace.put(coords.getTrace(), coords);
}
}
activate(DebuggerCoordinates.readDataState(tool, saveState, KEY_CURRENT_COORDS, false));
activate(DebuggerCoordinates.readDataState(tool, saveState, KEY_CURRENT_COORDS));
}
}
@@ -74,6 +74,10 @@ public class DefaultTransactionCoalescer<T extends UndoableDomainObject, U exten
@Override
public void close() {
synchronized (lock) {
if (tx == null) {
// TODO: This smells really bad
return;
}
tx.exit();
}
}
@@ -111,19 +111,6 @@ public interface DebuggerTraceManagerService {
*/
TraceThread getCurrentThread();
/**
* Get the active thread for a given trace
*
* <p>
* The manager remembers the last active thread for every open trace. If the trace has never
* been active, then the last active thread is null. If trace is the active trace, then this
* will return the currently active thread.
*
* @param trace the trace
* @return the thread, or null
*/
TraceThread getCurrentThreadFor(Trace trace);
/**
* Get the active snap
*
@@ -255,47 +242,125 @@ public interface DebuggerTraceManagerService {
*/
void activate(DebuggerCoordinates coordinates);
/**
* Resolve coordinates for the given trace using the manager's "best judgment"
*
* <p>
* The manager may use a variety of sources of context including the current trace, the last
* coordinates for a trace, the target's last/current focus, the list of active threads, etc.
*
* @param trace the trace
* @return the best coordinates
*/
DebuggerCoordinates resolveTrace(Trace trace);
/**
* Activate the given trace
*
* @param trace the desired trace
*/
void activateTrace(Trace trace);
default void activateTrace(Trace trace) {
activate(resolveTrace(trace));
}
/**
* Resolve coordinates for the given thread using the manager's "best judgment"
*
* @see #resolveTrace(Trace)
* @param thread the thread
* @return the best coordinates
*/
DebuggerCoordinates resolveThread(TraceThread thread);
/**
* Activate the given thread
*
* @param thread the desired thread
*/
void activateThread(TraceThread thread);
default void activateThread(TraceThread thread) {
activate(resolveThread(thread));
}
/**
* Resolve coordinates for the given snap using the manager's "best judgment"
*
* @see #resolveTrace(Trace)
* @param snap the snapshot key
* @return the best coordinates
*/
DebuggerCoordinates resolveSnap(long snap);
/**
* Activate the given snapshot key
*
* @param snap the desired snapshot key
*/
void activateSnap(long snap);
default void activateSnap(long snap) {
activate(resolveSnap(snap));
}
/**
* Resolve coordinates for the given time using the manager's "best judgment"
*
* @see #resolveTrace(Trace)
* @param time the time
* @return the best coordinates
*/
DebuggerCoordinates resolveTime(TraceSchedule time);
/**
* Activate the given point in time, possibly invoking emulation
*
* @param time the desired schedule
*/
void activateTime(TraceSchedule time);
default void activateTime(TraceSchedule time) {
activate(resolveTime(time));
}
/**
* Resolve coordinates for the given view using the manager's "best judgment"
*
* @see #resolveTrace(Trace)
* @param view the view
* @return the best coordinates
*/
DebuggerCoordinates resolveView(TraceProgramView view);
/**
* Resolve coordinates for the given frame level using the manager's "best judgment"
*
* @see #resolveTrace(Trace)
* @param frameLevel the frame level, 0 being the innermost
* @return the best coordinates
*/
DebuggerCoordinates resolveFrame(int frameLevel);
/**
* Activate the given stack frame
*
* @param frameLevel the level of the desired frame, 0 being innermost
*/
void activateFrame(int frameLevel);
default void activateFrame(int frameLevel) {
activate(resolveFrame(frameLevel));
}
/**
* Resolve coordinates for the given object using the manager's "best judgment"
*
* @see #resolveTrace(Trace)
* @param object the object
* @return the best coordinates
*/
DebuggerCoordinates resolveObject(TraceObject object);
/**
* Activate the given object
*
* @param object the desired object
*/
void activateObject(TraceObject object);
default void activateObject(TraceObject object) {
activate(resolveObject(object));
}
/**
* Control whether the trace manager automatically activates the "present snapshot"
@@ -413,14 +478,6 @@ public interface DebuggerTraceManagerService {
*/
void removeAutoCloseOnTerminateChangeListener(BooleanChangeAdapter listener);
/**
* Fill in an incomplete coordinate specification, using the manager's "best judgment"
*
* @param coords the possibly-incomplete coordinates
* @return the complete resolved coordinates
*/
DebuggerCoordinates resolveCoordinates(DebuggerCoordinates coordinates);
/**
* If the given coordinates are already materialized, get the snapshot
*
@@ -1138,8 +1138,7 @@ public interface FlatDebuggerAPI {
* @return the editor
*/
default StateEditor createStateEditor(DebuggerCoordinates coordinates) {
return getEditingService()
.createStateEditor(getTraceManager().resolveCoordinates(coordinates));
return getEditingService().createStateEditor(coordinates);
}
/**
@@ -1150,9 +1149,9 @@ public interface FlatDebuggerAPI {
* @return the editor
*/
default StateEditor createStateEditor(Trace trace, long snap) {
return getEditingService().createStateEditor(DebuggerCoordinates
.trace(trace)
.withSnap(snap));
return getEditingService().createStateEditor(getTraceManager()
.resolveTrace(trace)
.snap(snap));
}
/**
@@ -1164,10 +1163,10 @@ public interface FlatDebuggerAPI {
* @return the editor
*/
default StateEditor createStateEditor(TraceThread thread, int frame, long snap) {
return getEditingService().createStateEditor(DebuggerCoordinates
.thread(thread)
.withSnap(snap)
.withFrame(frame));
return getEditingService().createStateEditor(getTraceManager()
.resolveThread(thread)
.snap(snap)
.frame(frame));
}
/**
@@ -600,7 +600,7 @@ public abstract class AbstractGhidraHeadedDebuggerGUITest
modelService.addModel(mb.testModel);
}
protected TraceRecorder recordAndWaitSync() throws Exception {
protected TraceRecorder recordAndWaitSync() throws Throwable {
createTestModel();
mb.createTestProcessesAndThreads();
mb.createTestThreadRegisterBanks();
@@ -613,22 +613,7 @@ public abstract class AbstractGhidraHeadedDebuggerGUITest
TraceRecorder recorder = modelService.recordTarget(mb.testProcess1,
createTargetTraceMapper(mb.testProcess1), ActionSource.AUTOMATIC);
waitFor(() -> {
TraceThread thread = recorder.getTraceThread(mb.testThread1);
if (thread == null) {
return false;
}
/*
DebuggerRegisterMapper mapper = recorder.getRegisterMapper(thread);
if (mapper == null) {
return false;
}
if (!mapper.getRegistersOnTarget().containsAll(baseRegs)) {
return false;
}
*/
return true;
});
waitRecorder(recorder);
return recorder;
}
@@ -1343,7 +1343,7 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
regs.setValue(1, new RegisterValue(pc, new BigInteger("00404321", 16)));
}
waitForDomainObject(tb.trace);
traceManager.activate(DebuggerCoordinates.threadSnap(thread, 0));
traceManager.activate(DebuggerCoordinates.NOWHERE.thread(thread).snap(0));
waitForSwing();
assertEquals(tb.addr(0x00401234), listingProvider.getLocation().getAddress());
@@ -1398,7 +1398,7 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
regs.setValue(0, new RegisterValue(pc, new BigInteger("00401234", 16)));
}
waitForDomainObject(tb.trace);
traceManager.activate(DebuggerCoordinates.threadSnap(thread, 0));
traceManager.activate(DebuggerCoordinates.NOWHERE.thread(thread).snap(0));
waitForSwing();
assertEquals(tb.addr(0x00401234), listingProvider.getLocation().getAddress());
@@ -1432,7 +1432,7 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
stack.getFrame(0, true);
}
waitForDomainObject(tb.trace);
traceManager.activate(DebuggerCoordinates.threadSnap(thread, 0));
traceManager.activate(DebuggerCoordinates.NOWHERE.thread(thread).snap(0));
waitForSwing();
assertEquals(tb.addr(0x00401234), listingProvider.getLocation().getAddress());
@@ -945,7 +945,7 @@ public class DebuggerMemoryBytesProviderTest extends AbstractGhidraHeadedDebugge
regs.setValue(1, new RegisterValue(pc, new BigInteger("00404321", 16)));
}
waitForDomainObject(tb.trace);
traceManager.activate(DebuggerCoordinates.threadSnap(thread, 0));
traceManager.activate(DebuggerCoordinates.NOWHERE.thread(thread).snap(0));
waitForSwing();
assertEquals(tb.addr(0x00401234), memBytesProvider.getLocation().getAddress());
@@ -1000,7 +1000,7 @@ public class DebuggerMemoryBytesProviderTest extends AbstractGhidraHeadedDebugge
regs.setValue(0, new RegisterValue(pc, new BigInteger("00401234", 16)));
}
waitForDomainObject(tb.trace);
traceManager.activate(DebuggerCoordinates.threadSnap(thread, 0));
traceManager.activate(DebuggerCoordinates.NOWHERE.thread(thread).snap(0));
waitForSwing();
assertEquals(tb.addr(0x00401234), memBytesProvider.getLocation().getAddress());
@@ -1034,7 +1034,7 @@ public class DebuggerMemoryBytesProviderTest extends AbstractGhidraHeadedDebugge
stack.getFrame(0, true);
}
waitForDomainObject(tb.trace);
traceManager.activate(DebuggerCoordinates.threadSnap(thread, 0));
traceManager.activate(DebuggerCoordinates.NOWHERE.thread(thread).snap(0));
waitForSwing();
assertEquals(tb.addr(0x00401234), memBytesProvider.getLocation().getAddress());
@@ -182,7 +182,7 @@ public class DebuggerStaticMappingProviderTest extends AbstractGhidraHeadedDebug
// Select and remove the first 2 via the action
// NOTE: I'm not responsible for making the transaction here. The UI should do it.
mappingsProvider.mappingTable.getSelectionModel().setSelectionInterval(0, 1);
performAction(mappingsProvider.actionRemove);
performEnabledAction(mappingsProvider, mappingsProvider.actionRemove, true);
waitForDomainObject(tb.trace);
// Now, check that only the final one remains
@@ -291,7 +291,7 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
}
@Test
public void testLiveAddValuesThenActivatePopulatesPanel() throws Exception {
public void testLiveAddValuesThenActivatePopulatesPanel() throws Throwable {
TraceRecorder recorder = recordAndWaitSync();
traceManager.openTrace(recorder.getTrace());
waitForSwing();
@@ -725,8 +725,8 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
traceManager.activateSnap(1);
waitForSwing();
assertEquals(1, registersProvider.current.getSnap().longValue());
assertEquals(0, cloned.current.getSnap().longValue()); // TODO: Action to toggle snap tracking?
assertEquals(1, registersProvider.current.getSnap());
assertEquals(0, cloned.current.getSnap()); // TODO: Action to toggle snap tracking?
// NB, can't activate "null" trace. Manager ignores it.
traceManager.closeTrace(tb.trace);
@@ -566,6 +566,8 @@ public class DebuggerWatchesProviderTest extends AbstractGhidraHeadedDebuggerGUI
@Test
public void testEditMemoryTarget() throws Throwable {
WatchRow row = prepareTestEditTarget("*:8 r0");
// Wait for the async reads to settle.
waitForPass(() -> assertEquals(tb.addr(0x00400000), row.getAddress()));
row.setRawValueString("0x1234");
retryVoid(() -> {
@@ -1293,11 +1293,17 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe
}
waitForDomainObject(trace);
waitOn(mappingService.changesSettled());
waitOn(breakpointService.changesSettled());
// Sanity
assertLogicalBreakpointForMappedSoftwareBreakpoint(trace);
expectMappingChange(() -> undo(trace));
waitOn(mappingService.changesSettled());
waitOn(breakpointService.changesSettled());
// NB. The bookmark remains
LogicalBreakpoint one = Unique.assertOne(breakpointService.getAllBreakpoints());
assertTrue(one.getTraceBreakpoints().isEmpty());
@@ -138,20 +138,20 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
@Test
public void testWriteEmuMemoryAfterStep() throws Throwable {
createAndOpenTrace();
editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_EMULATOR);
editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_TRACE);
try (UndoableTransaction tid = tb.startTransaction()) {
// NB. TraceManager should automatically activate the first thread
TraceThread thread = tb.getOrAddThread("Threads[0]", 0);
AsyncPcodeExecutor<byte[]> executor =
TracePcodeUtils.executorForCoordinates(
DebuggerCoordinates.all(tb.trace, null, thread, null, TraceSchedule.ZERO, 0,
null));
TracePcodeUtils.executorForCoordinates(DebuggerCoordinates.NOWHERE.thread(thread));
Assembler asm = Assemblers.getAssembler(tb.trace.getFixedProgramView(0));
asm.assemble(tb.addr(0x00400000), "imm r0,#123");
executor.executeSleighLine("pc = 0x00400000");
}
traceManager.activateTrace(tb.trace);
editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_EMULATOR);
waitForSwing();
TraceSchedule step1 = TraceSchedule.parse("0:t0-1");
@@ -163,7 +163,7 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
waitForSwing();
DebuggerCoordinates current = traceManager.getCurrent();
assertEquals(0, current.getSnap().longValue()); // Chain edits, don't source from scratch
assertEquals(0, current.getSnap()); // Chain edits, don't source from scratch
long snap = current.getViewSnap();
assertTrue(DBTraceUtils.isScratch(snap));
@@ -175,21 +175,21 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
@Test
public void testWriteEmuRegisterAfterStep() throws Throwable {
createAndOpenTrace();
editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_EMULATOR);
editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_TRACE);
TraceThread thread;
try (UndoableTransaction tid = tb.startTransaction()) {
// NB. TraceManager should automatically activate the first thread
thread = tb.getOrAddThread("Threads[0]", 0);
AsyncPcodeExecutor<byte[]> executor =
TracePcodeUtils.executorForCoordinates(
DebuggerCoordinates.all(tb.trace, null, thread, null, TraceSchedule.ZERO, 0,
null));
TracePcodeUtils.executorForCoordinates(DebuggerCoordinates.NOWHERE.thread(thread));
Assembler asm = Assemblers.getAssembler(tb.trace.getFixedProgramView(0));
asm.assemble(tb.addr(0x00400000), "imm r0,#123");
executor.executeSleighLine("pc = 0x00400000");
}
traceManager.activateTrace(tb.trace);
editingService.setCurrentMode(tb.trace, StateEditingMode.WRITE_EMULATOR);
waitForSwing();
TraceSchedule step1 = TraceSchedule.parse("0:t0-1");
@@ -201,7 +201,7 @@ public class DebuggerStateEditingServiceTest extends AbstractGhidraHeadedDebugge
waitForSwing();
DebuggerCoordinates current = traceManager.getCurrent();
assertEquals(0, current.getSnap().longValue()); // Chain edits, don't source from scratch
assertEquals(0, current.getSnap()); // Chain edits, don't source from scratch
long snap = current.getViewSnap();
assertTrue(DBTraceUtils.isScratch(snap));
@@ -133,12 +133,12 @@ public class DebuggerTraceManagerServiceTest extends AbstractGhidraHeadedDebugge
waitForSwing();
assertNull(traceManager.getCurrentTrace());
assertEquals(thread, traceManager.getCurrentThreadFor(tb.trace));
assertEquals(thread, traceManager.getCurrentFor(tb.trace).getThread());
traceManager.closeTrace(tb.trace);
waitForSwing();
assertNull(traceManager.getCurrentThreadFor(tb.trace));
assertNull(traceManager.getCurrentFor(tb.trace).getThread());
}
@Test
@@ -159,13 +159,13 @@ public class FlatDebuggerAPITest extends AbstractGhidraHeadedDebuggerGUITest {
@Test
public void testGetCurrentDebuggerCoordinates() throws Throwable {
assertEquals(DebuggerCoordinates.NOWHERE, flat.getCurrentDebuggerCoordinates());
assertSame(DebuggerCoordinates.NOWHERE, flat.getCurrentDebuggerCoordinates());
createAndOpenTrace();
traceManager.activateTrace(tb.trace);
assertEquals(DebuggerCoordinates.all(tb.trace, null, null, tb.trace.getProgramView(),
TraceSchedule.parse("0"), 0, null), flat.getCurrentDebuggerCoordinates());
assertEquals(DebuggerCoordinates.NOWHERE.trace(tb.trace),
flat.getCurrentDebuggerCoordinates());
}
@Test
@@ -318,8 +318,8 @@ public class FlatDebuggerAPITest extends AbstractGhidraHeadedDebuggerGUITest {
waitForSwing();
assertEquals(thread, traceManager.getCurrentThread());
flat.activateThread(null); // Ignored by manager
assertEquals(thread, traceManager.getCurrentThread());
flat.activateThread(null);
assertNull(traceManager.getCurrentThread());
}
@Test
@@ -820,7 +820,7 @@ public class FlatDebuggerAPITest extends AbstractGhidraHeadedDebuggerGUITest {
TraceRecorder recorder = record(mb.testProcess1);
waitRecorder(recorder);
mb.testModel.requestFocus(mb.testThread2);
waitOn(mb.testModel.requestFocus(mb.testThread2));
waitRecorder(recorder);
assertEquals(mb.testThread2, flat.getTargetFocus(recorder.getTrace()));
@@ -849,7 +849,7 @@ public class FlatDebuggerAPITest extends AbstractGhidraHeadedDebuggerGUITest {
createTestModel();
mb.createTestProcessesAndThreads();
TraceRecorder recorder = record(mb.testProcess1);
recorder.requestFocus(mb.testThread2);
assertTrue(waitOn(recorder.requestFocus(mb.testThread2)));
waitRecorder(recorder);
assertTrue(step.apply(flat));
@@ -899,7 +899,7 @@ public class FlatDebuggerAPITest extends AbstractGhidraHeadedDebuggerGUITest {
createTestModel();
mb.createTestProcessesAndThreads();
TraceRecorder recorder = record(mb.testProcess1);
recorder.requestFocus(mb.testThread2);
assertTrue(waitOn(recorder.requestFocus(mb.testThread2)));
waitRecorder(recorder);
assertTrue(resume.apply(flat));
@@ -933,7 +933,7 @@ public class FlatDebuggerAPITest extends AbstractGhidraHeadedDebuggerGUITest {
@Override
public CompletableFuture<Void> interrupt() {
observedThread = this;
return super.resume();
return super.interrupt();
}
};
}
@@ -942,7 +942,7 @@ public class FlatDebuggerAPITest extends AbstractGhidraHeadedDebuggerGUITest {
createTestModel();
mb.createTestProcessesAndThreads();
TraceRecorder recorder = record(mb.testProcess1);
recorder.requestFocus(mb.testThread2);
assertTrue(waitOn(recorder.requestFocus(mb.testThread2)));
waitRecorder(recorder);
assertTrue(interrupt.apply(flat));
@@ -976,7 +976,7 @@ public class FlatDebuggerAPITest extends AbstractGhidraHeadedDebuggerGUITest {
@Override
public CompletableFuture<Void> kill() {
observedThread = this;
return super.resume();
return super.kill();
}
};
}
@@ -985,7 +985,7 @@ public class FlatDebuggerAPITest extends AbstractGhidraHeadedDebuggerGUITest {
createTestModel();
mb.createTestProcessesAndThreads();
TraceRecorder recorder = record(mb.testProcess1);
recorder.requestFocus(mb.testThread2);
assertTrue(waitOn(recorder.requestFocus(mb.testThread2)));
waitRecorder(recorder);
assertTrue(kill.apply(flat));
@@ -1026,7 +1026,7 @@ public class FlatDebuggerAPITest extends AbstractGhidraHeadedDebuggerGUITest {
createTestModel();
mb.createTestProcessesAndThreads();
TraceRecorder recorder = record(mb.testProcess1);
recorder.requestFocus(mb.testThread2);
assertTrue(waitOn(recorder.requestFocus(mb.testThread2)));
waitRecorder(recorder);
assertEquals("Response to cmd", executeCapture.apply(flat, "cmd"));
@@ -68,8 +68,8 @@ public class TestTargetSession extends DefaultTargetModelRoot
@Override
public CompletableFuture<Void> requestFocus(TargetObject obj) {
return model.gateFuture(getModel().future(null).thenAccept(__ -> {
changeAttributes(List.of(), List.of(), Map.of(FOCUS_ATTRIBUTE_NAME, obj //
), "Focus requested");
changeAttributes(List.of(), List.of(), Map.of(FOCUS_ATTRIBUTE_NAME, obj),
"Focus requested");
}));
}
@@ -205,10 +205,8 @@ public class DBTraceObjectStack implements TraceObjectStack, DBTraceObjectInterf
return doGetFrame(level);
}
}
else {
try (LockHold hold = object.getTrace().lockRead()) {
return doGetFrame(level);
}
try (LockHold hold = object.getTrace().lockRead()) {
return doGetFrame(level);
}
}
@@ -327,6 +327,9 @@ public class DBTraceObjectValue extends DBAnnotatedObject implements InternalTra
}
protected TraceObjectKeyPath doGetCanonicalPath() {
if (triple == null || triple.parent == null) {
return TraceObjectKeyPath.of();
}
return triple.parent.getCanonicalPath().extend(triple.key);
}