From 2c0678ed83e3fb280f4f9c58554826fd21370f8f Mon Sep 17 00:00:00 2001 From: Dan <46821332+nsadeveloper789@users.noreply.github.com> Date: Fri, 19 Aug 2022 10:00:10 -0400 Subject: [PATCH] GP-2448: Sync DebuggerModelProvider to tool --- .../core/debug/DebuggerCoordinates.java | 515 ++++++++++++------ ...CurrentPlatformTraceDisassembleAction.java | 16 +- .../DebuggerCopyIntoProgramDialog.java | 2 +- .../gui/diff/DebuggerTraceViewDiffPlugin.java | 5 +- .../gui/listing/DebuggerListingProvider.java | 5 +- .../memory/DebuggerMemoryBytesProvider.java | 5 +- .../memview/DebuggerMemviewTraceListener.java | 3 +- .../gui/model/AbstractQueryTableModel.java | 3 + .../gui/model/AbstractQueryTablePanel.java | 16 +- .../gui/model/DebuggerModelProvider.java | 147 ++++- .../debug/gui/model/ObjectTableModel.java | 10 + .../core/debug/gui/model/ObjectTreeModel.java | 9 + .../debug/gui/model/ObjectsTreePanel.java | 18 +- .../core/debug/gui/model/PathTableModel.java | 18 +- .../register/DebuggerRegistersProvider.java | 4 +- .../debug/gui/time/DebuggerTimeProvider.java | 2 +- .../DebuggerStateEditingServicePlugin.java | 27 +- .../service/model/record/ObjectRecorder.java | 3 + .../DebuggerTraceManagerServicePlugin.java | 243 +++------ .../utils/DefaultTransactionCoalescer.java | 4 + .../services/DebuggerTraceManagerService.java | 111 +++- .../ghidra/debug/flatapi/FlatDebuggerAPI.java | 17 +- .../AbstractGhidraHeadedDebuggerGUITest.java | 19 +- .../listing/DebuggerListingProviderTest.java | 6 +- .../DebuggerMemoryBytesProviderTest.java | 6 +- .../gui/model/DebuggerModelProviderTest.java | 435 ++++++++++++--- .../DebuggerStaticMappingProviderTest.java | 2 +- .../DebuggerRegistersProviderTest.java | 6 +- .../watch/DebuggerWatchesProviderTest.java | 2 + .../DebuggerLogicalBreakpointServiceTest.java | 6 + .../DebuggerStateEditingServiceTest.java | 20 +- .../DebuggerTraceManagerServiceTest.java | 4 +- .../debug/flatapi/FlatDebuggerAPITest.java | 26 +- .../ghidra/dbg/model/TestTargetSession.java | 4 +- .../database/stack/DBTraceObjectStack.java | 6 +- .../database/target/DBTraceObjectValue.java | 3 + 36 files changed, 1159 insertions(+), 569 deletions(-) diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/DebuggerCoordinates.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/DebuggerCoordinates.java index 237ebac808..c279f1ed6d 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/DebuggerCoordinates.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/DebuggerCoordinates.java @@ -21,8 +21,11 @@ import java.util.Objects; import org.jdom.Element; +import com.google.common.collect.Range; + import ghidra.app.services.DebuggerTraceManagerService; import ghidra.app.services.TraceRecorder; +import ghidra.dbg.target.TargetObject; import ghidra.framework.data.ProjectFileManager; import ghidra.framework.model.*; import ghidra.framework.options.SaveState; @@ -31,7 +34,7 @@ import ghidra.trace.database.DBTraceContentHandler; import ghidra.trace.database.DBTraceUtils; import ghidra.trace.model.Trace; import ghidra.trace.model.program.TraceProgramView; -import ghidra.trace.model.stack.TraceObjectStackFrame; +import ghidra.trace.model.stack.*; import ghidra.trace.model.target.TraceObject; import ghidra.trace.model.target.TraceObjectKeyPath; import ghidra.trace.model.thread.TraceObjectThread; @@ -46,7 +49,7 @@ import ghidra.util.NotOwnerException; public class DebuggerCoordinates { public static final DebuggerCoordinates NOWHERE = - new DebuggerCoordinates(null, null, null, null, TraceSchedule.ZERO, 0, null); + new DebuggerCoordinates(null, null, null, null, null, null, null); private static final String KEY_TRACE_PROJ_LOC = "TraceProjLoc"; private static final String KEY_TRACE_PROJ_NAME = "TraceProjName"; @@ -57,94 +60,6 @@ public class DebuggerCoordinates { private static final String KEY_FRAME = "Frame"; private static final String KEY_OBJ_PATH = "ObjectPath"; - public static DebuggerCoordinates all(Trace trace, TraceRecorder recorder, TraceThread thread, - TraceProgramView view, TraceSchedule time, Integer frame, TraceObject object) { - if (trace == NOWHERE.trace && recorder == NOWHERE.recorder && thread == NOWHERE.thread && - view == NOWHERE.view && time == NOWHERE.time && frame == NOWHERE.frame && - object == NOWHERE.object) { - return NOWHERE; - } - return new DebuggerCoordinates(trace, recorder, thread, view, time, frame, object); - } - - public static DebuggerCoordinates trace(Trace trace) { - if (trace == null) { - return NOWHERE; - } - return all(trace, null, null, null, null, null, null); - } - - public static DebuggerCoordinates recorder(TraceRecorder recorder) { - return all(recorder == null ? null : recorder.getTrace(), recorder, - null, null, recorder == null ? null : TraceSchedule.snap(recorder.getSnap()), null, - null); - } - - public static DebuggerCoordinates thread(TraceThread thread) { - return all(thread == null ? null : thread.getTrace(), null, thread, null, null, null, null); - } - - public static DebuggerCoordinates rawView(TraceProgramView view) { - return all(view.getTrace(), null, null, view, TraceSchedule.snap(view.getSnap()), null, - null); - } - - public static DebuggerCoordinates view(TraceProgramView view) { - if (view == null) { - return NOWHERE; - } - long snap = view.getSnap(); - if (!DBTraceUtils.isScratch(snap)) { - return rawView(view); - } - Trace trace = view.getTrace(); - TraceSnapshot snapshot = trace.getTimeManager().getSnapshot(snap, false); - if (snapshot == null) { - return rawView(view); - } - TraceSchedule schedule = snapshot.getSchedule(); - if (schedule == null) { - return rawView(view); - } - return trace(trace).withTime(schedule); - } - - public static DebuggerCoordinates snap(long snap) { - return all(null, null, null, null, TraceSchedule.snap(snap), null, null); - } - - public static DebuggerCoordinates time(String time) { - return time(TraceSchedule.parse(time)); - } - - public static DebuggerCoordinates time(TraceSchedule time) { - return all(null, null, null, null, time, null, null); - } - - public static DebuggerCoordinates frame(int frame) { - return all(null, null, null, null, null, frame, null); - } - - public static DebuggerCoordinates object(TraceObject object) { - if (object == null) { - return NOWHERE; - } - TraceObjectThread thread = object.queryCanonicalAncestorsInterface(TraceObjectThread.class) - .findFirst() - .orElse(null); - TraceObjectStackFrame frame = - object.queryCanonicalAncestorsInterface(TraceObjectStackFrame.class) - .findFirst() - .orElse(null); - return all(object.getTrace(), null, thread, null, null, - frame == null ? null : frame.getLevel(), object); - } - - public static DebuggerCoordinates threadSnap(TraceThread thread, long snap) { - return all(thread == null ? null : thread.getTrace(), null, thread, null, - TraceSchedule.snap(snap), null, null); - } - public static boolean equalsIgnoreRecorderAndView(DebuggerCoordinates a, DebuggerCoordinates b) { if (!Objects.equals(a.trace, b.trace)) { @@ -153,10 +68,14 @@ public class DebuggerCoordinates { if (!Objects.equals(a.thread, b.thread)) { return false; } - if (!Objects.equals(a.time, b.time)) { + // Consider defaults + if (!Objects.equals(a.getTime(), b.getTime())) { return false; } - if (!Objects.equals(a.frame, b.frame)) { + if (!Objects.equals(a.getFrame(), b.getFrame())) { + return false; + } + if (!Objects.equals(a.getObject(), b.getObject())) { return false; } return true; @@ -175,7 +94,7 @@ public class DebuggerCoordinates { private Long viewSnap; private DefaultTraceTimeViewport viewport; - protected DebuggerCoordinates(Trace trace, TraceRecorder recorder, TraceThread thread, + DebuggerCoordinates(Trace trace, TraceRecorder recorder, TraceThread thread, TraceProgramView view, TraceSchedule time, Integer frame, TraceObject object) { this.trace = trace; this.recorder = recorder; @@ -191,7 +110,7 @@ public class DebuggerCoordinates { @Override public String toString() { return String.format( - "Coords(trace=%s,recorder=%s,thread=%s,view=%s,time=%s,frame=%d,object=%d)", + "Coords(trace=%s,recorder=%s,thread=%s,view=%s,time=%s,frame=%d,object=%s)", trace, recorder, thread, view, time, frame, object); } @@ -213,6 +132,7 @@ public class DebuggerCoordinates { if (!Objects.equals(this.view, that.view)) { return false; } + // Do not consider defaults if (!Objects.equals(this.time, that.time)) { return false; } @@ -222,7 +142,7 @@ public class DebuggerCoordinates { if (!Objects.equals(this.object, that.object)) { return false; } - + return true; } @@ -231,6 +151,325 @@ public class DebuggerCoordinates { return hash; } + private static TraceThread resolveThread(Trace trace, TraceSchedule time) { + long snap = time.getSnap(); + return trace.getThreadManager() + .getLiveThreads(snap) + .stream() + .findFirst() + .orElse(null); + } + + private static TraceThread resolveThread(Trace trace) { + return resolveThread(trace, TraceSchedule.ZERO); + } + + private static TraceObject resolveObject(Trace trace) { + return trace.getObjectManager().getRootObject(); + } + + private static TraceProgramView resolveView(Trace trace, TraceSchedule time) { + // TODO: Allow multiple times viewed of the same trace? (Aside from snap compare) + // Trace manager will adjust the view's snap to match coordinates + return trace.getProgramView(); + } + + private static TraceProgramView resolveView(Trace trace) { + return resolveView(trace, TraceSchedule.ZERO); + } + + public DebuggerCoordinates trace(Trace newTrace) { + if (newTrace == null) { + return NOWHERE; + } + if (trace == newTrace) { + return this; + } + if (trace == null) { + TraceThread newThread = resolveThread(newTrace); + TraceProgramView newView = resolveView(newTrace); + TraceSchedule newTime = null; // Allow later resolution + Integer newFrame = resolveFrame(newThread, newTime); + TraceObject newObject = resolveObject(newTrace); + return new DebuggerCoordinates(newTrace, null, newThread, newView, newTime, newFrame, + newObject); + } + throw new IllegalArgumentException("Cannot change trace"); + } + + private static TraceThread resolveThread(TraceRecorder recorder, TraceSchedule time) { + if (recorder.getSnap() != time.getSnap() || !recorder.isSupportsFocus()) { + return resolveThread(recorder.getTrace(), time); + } + return resolveThread(recorder, recorder.getFocus()); + } + + private static TraceThread resolveThread(Trace trace, TraceRecorder recorder, + TraceSchedule time) { + if (recorder == null) { + return resolveThread(trace, time); + } + return resolveThread(recorder, time); + } + + private static Integer resolveFrame(TraceThread thread, TraceSchedule time) { + // Use null to allow later resolution. Getter will default to 0 + return null; + } + + private static Integer resolveFrame(TraceRecorder recorder, TraceThread thread, + TraceSchedule time) { + if (recorder == null || recorder.getSnap() != time.getSnap() || + !recorder.isSupportsFocus()) { + return resolveFrame(thread, time); + } + return resolveFrame(recorder, recorder.getFocus()); + } + + private static TraceObject resolveObject(Trace trace, TargetObject object) { + if (object == null) { + return null; + } + return trace.getObjectManager() + .getObjectByCanonicalPath(TraceObjectKeyPath.of(object.getPath())); + } + + private static TraceObject resolveObject(TraceRecorder recorder, TraceSchedule time) { + if (recorder.getSnap() != time.getSnap() || !recorder.isSupportsFocus()) { + return resolveObject(recorder.getTrace()); + } + return resolveObject(recorder.getTrace(), recorder.getFocus()); + } + + public DebuggerCoordinates recorder(TraceRecorder newRecorder) { + if (recorder == newRecorder) { + return this; + } + if (newRecorder == null) { + return new DebuggerCoordinates(trace, newRecorder, thread, view, time, frame, object); + } + if (newRecorder != null && trace != null && newRecorder.getTrace() != trace) { + throw new IllegalArgumentException("Cannot change trace"); + } + Trace newTrace = trace != null ? trace : newRecorder.getTrace(); + TraceSchedule newTime = time != null ? time : TraceSchedule.snap(newRecorder.getSnap()); + TraceThread newThread = thread != null ? thread : resolveThread(newRecorder, newTime); + TraceProgramView newView = view != null ? view : resolveView(newTrace, newTime); + Integer newFrame = frame != null ? frame : resolveFrame(newRecorder, newThread, newTime); + TraceObject newObject = object != null ? object : resolveObject(newRecorder, newTime); + return new DebuggerCoordinates(newTrace, newRecorder, newThread, newView, newTime, newFrame, + newObject); + } + + public DebuggerCoordinates reFindThread() { + if (trace == null || thread == null) { + return this; + } + return thread(trace.getThreadManager().getThread(thread.getKey())); + } + + private static TraceObject resolveObject(TraceThread thread, Integer frameLevel, + TraceSchedule time) { + if (thread instanceof TraceObjectThread tot) { + TraceObject objThread = tot.getObject(); + if (frameLevel == null) { + return objThread; + } + TraceStack stack = + thread.getTrace().getStackManager().getStack(thread, time.getSnap(), false); + if (stack == null) { + return objThread; + } + TraceStackFrame frame = stack.getFrame(frameLevel, false); + if (frame == null) { + return objThread; + } + return ((TraceObjectStackFrame) frame).getObject(); + } + return null; + } + + /** + * Check if the object is a canonical ancestor + * + * @param ancestor the proposed ancestor + * @param successor the proposed successor + * @param time the time to consider (only the snap matters) + * @return true if ancestor is in fact an ancestor of successor at the given time + */ + private static boolean isAncestor(TraceObject ancestor, TraceObject successor, + TraceSchedule time) { + return successor.getCanonicalParents(Range.singleton(time.getSnap())) + .anyMatch(p -> p == ancestor); + } + + public DebuggerCoordinates thread(TraceThread newThread) { + if (thread == newThread) { + return this; + } + if (newThread != null && trace != null && trace != newThread.getTrace()) { + throw new IllegalArgumentException("Cannot change trace"); + } + if (newThread == null) { + newThread = resolveThread(recorder, getTime()); + } + Trace newTrace = trace != null ? trace : newThread.getTrace(); + TraceSchedule newTime = time != null ? time : resolveTime(view); + TraceProgramView newView = view != null ? view : resolveView(newTrace, newTime); + // Yes, override frame with 0 on thread changes, unless target says otherwise + Integer newFrame = resolveFrame(recorder, newThread, newTime); + // Yes, forced frame change may also force object change + TraceObject ancestor = resolveObject(newThread, newFrame, newTime); + TraceObject newObject = + object != null && isAncestor(ancestor, object, newTime) ? object : ancestor; + return new DebuggerCoordinates(newTrace, recorder, newThread, newView, newTime, newFrame, + newObject); + } + + /** + * Get these same coordinates with time replaced by the given snap-only schedule + * + * @param snap the new snap + * @return the new coordinates + */ + public DebuggerCoordinates snap(long snap) { + return time(TraceSchedule.snap(snap)); + } + + public DebuggerCoordinates time(TraceSchedule newTime) { + if (trace == null) { + return NOWHERE; + } + long snap = newTime.getSnap(); + TraceThread newThread = thread != null && thread.getLifespan().contains(snap) ? thread + : resolveThread(trace, recorder, newTime); + // This will cause the frame to reset to 0 on every snap change. That's fair.... + Integer newFrame = resolveFrame(newThread, newTime); + TraceObject ancestor = resolveObject(newThread, newFrame, newTime); + TraceObject newObject = + object != null && isAncestor(ancestor, object, newTime) ? object : ancestor; + return new DebuggerCoordinates(trace, recorder, newThread, view, newTime, newFrame, + newObject); + } + + public DebuggerCoordinates frame(int newFrame) { + if (trace == null) { + return NOWHERE; + } + if (Objects.equals(frame, newFrame)) { + return this; + } + TraceObject ancestor = resolveObject(thread, newFrame, getTime()); + TraceObject newObject = + object != null && isAncestor(ancestor, object, getTime()) ? object : ancestor; + return new DebuggerCoordinates(trace, recorder, thread, view, time, newFrame, newObject); + } + + private DebuggerCoordinates replaceView(TraceProgramView newView) { + return new DebuggerCoordinates(trace, recorder, thread, newView, time, frame, object); + } + + private static TraceSchedule resolveTime(TraceProgramView view) { + if (view == null) { + return null; + } + long snap = view.getSnap(); + if (!DBTraceUtils.isScratch(snap)) { + return TraceSchedule.snap(snap); + } + TraceSnapshot snapshot = view.getTrace().getTimeManager().getSnapshot(snap, false); + if (snapshot == null) { + return TraceSchedule.snap(snap); + } + TraceSchedule schedule = snapshot.getSchedule(); + if (schedule == null) { + return TraceSchedule.snap(snap); + } + return schedule; + } + + public DebuggerCoordinates view(TraceProgramView newView) { + if (view == newView) { + return this; + } + if (trace == null) { + if (newView == null) { + return NOWHERE; + } + return NOWHERE.trace(newView.getTrace()) + .time(resolveTime(newView)) + .replaceView(newView); + } + if (newView.getTrace() != trace) { + throw new IllegalArgumentException("Cannot change trace"); + } + return time(resolveTime(newView)).replaceView(newView); + } + + private static TraceThread resolveThread(TraceObject object) { + return object.queryCanonicalAncestorsInterface(TraceObjectThread.class) + .findFirst() + .orElse(null); + } + + private static Integer resolveFrame(TraceObject object) { + TraceObjectStackFrame frame = + object.queryCanonicalAncestorsInterface(TraceObjectStackFrame.class) + .findFirst() + .orElse(null); + return frame == null ? null : frame.getLevel(); + } + + public DebuggerCoordinates object(TraceObject newObject) { + Trace newTrace; + if (trace == null) { + if (newObject == null) { + return NOWHERE; + } + newTrace = newObject.getTrace(); + } + else { + if (newObject == null) { + return new DebuggerCoordinates(trace, recorder, thread, view, time, frame, + newObject); + } + if (newObject.getTrace() != trace) { + throw new IllegalArgumentException("Cannot change trace"); + } + newTrace = trace; + } + TraceThread newThread = resolveThread(newObject); + Integer newFrame = resolveFrame(newObject); + + return new DebuggerCoordinates(newTrace, recorder, newThread, view, time, newFrame, + newObject); + } + + protected static TraceThread resolveThread(TraceRecorder recorder, TargetObject targetObject) { + return recorder.getTraceThreadForSuccessor(targetObject); + } + + protected static Integer resolveFrame(TraceRecorder recorder, TargetObject targetObject) { + TraceStackFrame frame = recorder.getTraceStackFrameForSuccessor(targetObject); + return frame == null ? null : frame.getLevel(); + } + + protected DebuggerCoordinates object(TraceObject traceObject, TargetObject targetObject) { + if (traceObject != null) { + return object(traceObject); + } + if (recorder == null) { + throw new IllegalArgumentException("No recorder"); + } + TraceThread newThread = resolveThread(recorder, targetObject); + Integer newFrame = resolveFrame(recorder, targetObject); + return new DebuggerCoordinates(trace, recorder, newThread, view, time, newFrame, null); + } + + public DebuggerCoordinates object(TargetObject newObject) { + return object(resolveObject(trace, newObject), newObject); + } + public Trace getTrace() { return trace; } @@ -239,70 +478,24 @@ public class DebuggerCoordinates { return recorder; } - public DebuggerCoordinates withRecorder(TraceRecorder newRecorder) { - return all(trace, newRecorder, thread, view, time, frame, object); - } - public TraceThread getThread() { return thread; } - public DebuggerCoordinates withReFoundThread() { - if (trace == null || thread == null) { - return this; - } - TraceThread newThread = trace.getThreadManager().getThread(thread.getKey()); - if (thread == newThread) { - return this; - } - return withThread(newThread); - } - - public DebuggerCoordinates withThread(TraceThread newThread) { - return all(trace, recorder, newThread, view, time, frame, object); - } - public TraceProgramView getView() { return view; } - public Long getSnap() { - return time.getSnap(); - } - - /** - * Get these same coordinates with time replaced by the given snap-only coordinate - * - * @param newSnap the new snap - * @return the new coordinates - */ - public DebuggerCoordinates withSnap(Long newSnap) { - return all(trace, recorder, thread, view, - newSnap == null ? time : TraceSchedule.snap(newSnap), frame, object); - } - - public DebuggerCoordinates withTime(TraceSchedule newTime) { - return all(trace, recorder, thread, view, newTime, frame, object); - } - - public DebuggerCoordinates withFrame(int newFrame) { - return all(trace, recorder, thread, view, time, newFrame, object); - } - - public DebuggerCoordinates withView(TraceProgramView newView) { - return all(trace, recorder, thread, newView, time, frame, object); - } - - public DebuggerCoordinates withObject(TraceObject newObject) { - return all(trace, recorder, thread, view, time, frame, newObject); + public long getSnap() { + return getTime().getSnap(); } public TraceSchedule getTime() { - return time; + return time == null ? TraceSchedule.ZERO : time; } - public Integer getFrame() { - return frame; + public int getFrame() { + return frame == null ? 0 : frame; } public TraceObject getObject() { @@ -313,16 +506,17 @@ public class DebuggerCoordinates { if (viewSnap != null) { return viewSnap; } - if (time.isSnapOnly()) { - return viewSnap = time.getSnap(); + TraceSchedule defaultedTime = getTime(); + if (defaultedTime.isSnapOnly()) { + return viewSnap = defaultedTime.getSnap(); } Collection snapshots = - trace.getTimeManager().getSnapshotsWithSchedule(time); + trace.getTimeManager().getSnapshotsWithSchedule(defaultedTime); if (snapshots.isEmpty()) { Msg.warn(this, "Seems the emulation service did not create the requested snapshot, yet"); // NB. Don't cache viewSnap. Maybe next time, we'll get it. - return time.getSnap(); + return defaultedTime.getSnap(); } return viewSnap = snapshots.iterator().next().getKey(); } @@ -414,7 +608,7 @@ public class DebuggerCoordinates { } public static DebuggerCoordinates readDataState(PluginTool tool, SaveState saveState, - String key, boolean resolve) { + String key) { if (!saveState.hasValue(key)) { return NOWHERE; } @@ -443,7 +637,7 @@ public class DebuggerCoordinates { catch (Exception e) { Msg.error(DebuggerCoordinates.class, "Could not restore invalid time specification: " + timeSpec); - time = TraceSchedule.ZERO; + time = null; } Integer frame = null; if (coordState.hasValue(KEY_FRAME)) { @@ -462,12 +656,12 @@ public class DebuggerCoordinates { } } - DebuggerCoordinates coords = - DebuggerCoordinates.all(trace, null, thread, null, time, frame, object); - if (!resolve) { - return coords; - } - return traceManager.resolveCoordinates(coords); + DebuggerCoordinates coords = DebuggerCoordinates.NOWHERE.trace(trace) + .thread(thread) + .time(time) + .frame(frame) + .object(object); + return coords; } public boolean isAlive() { @@ -475,11 +669,12 @@ public class DebuggerCoordinates { } public boolean isPresent() { - return recorder.getSnap() == time.getSnap() && time.isSnapOnly(); + TraceSchedule defaultedTime = getTime(); + return recorder.getSnap() == defaultedTime.getSnap() && defaultedTime.isSnapOnly(); } public boolean isReadsPresent() { - return recorder.getSnap() == time.getSnap(); + return recorder.getSnap() == getTime().getSnap(); } public boolean isAliveAndPresent() { diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/CurrentPlatformTraceDisassembleAction.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/CurrentPlatformTraceDisassembleAction.java index 23419dbd35..7729d36adb 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/CurrentPlatformTraceDisassembleAction.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/disassemble/CurrentPlatformTraceDisassembleAction.java @@ -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); diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/copying/DebuggerCopyIntoProgramDialog.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/copying/DebuggerCopyIntoProgramDialog.java index 27b82790d1..032d83bf09 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/copying/DebuggerCopyIntoProgramDialog.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/copying/DebuggerCopyIntoProgramDialog.java @@ -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; diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/diff/DebuggerTraceViewDiffPlugin.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/diff/DebuggerTraceViewDiffPlugin.java index bba465234e..44eca5be13 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/diff/DebuggerTraceViewDiffPlugin.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/diff/DebuggerTraceViewDiffPlugin.java @@ -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 -> { diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingProvider.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingProvider.java index a42cead705..319a161c3e 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingProvider.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingProvider.java @@ -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) { diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesProvider.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesProvider.java index 9e8ab61930..67d6db2267 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesProvider.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesProvider.java @@ -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); diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memview/DebuggerMemviewTraceListener.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memview/DebuggerMemviewTraceListener.java index 10fec2c8c8..d7ff2933e0 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memview/DebuggerMemviewTraceListener.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memview/DebuggerMemviewTraceListener.java @@ -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) { diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/AbstractQueryTableModel.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/AbstractQueryTableModel.java index 80d873db9c..d66c0a2ddc 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/AbstractQueryTableModel.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/AbstractQueryTableModel.java @@ -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 extends ThreadedTableModel extends JPanel { protected final AbstractQueryTableModel tableModel; protected final GhidraTable table; - private final GhidraTableFilterPanel filterPanel; + protected final GhidraTableFilterPanel filterPanel; protected DebuggerCoordinates current = DebuggerCoordinates.NOWHERE; protected boolean limitToSnap = false; @@ -60,6 +61,10 @@ public abstract class AbstractQueryTablePanel 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 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 getSelectedItems() { return filterPanel.getSelectedItems(); } diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/DebuggerModelProvider.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/DebuggerModelProvider.java index 85dab75099..05f45deb13 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/DebuggerModelProvider.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/DebuggerModelProvider.java @@ -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); } diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/ObjectTableModel.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/ObjectTableModel.java index 157bddde06..6a4ec73473 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/ObjectTableModel.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/ObjectTableModel.java @@ -394,6 +394,16 @@ public class ObjectTableModel extends AbstractQueryTableModel { 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); diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/ObjectTreeModel.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/ObjectTreeModel.java index b68a1b9a31..0e6ffce8b4 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/ObjectTreeModel.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/ObjectTreeModel.java @@ -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(); } diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/ObjectsTreePanel.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/ObjectsTreePanel.java index 2cff8c9089..2e81bd1708 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/ObjectsTreePanel.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/ObjectsTreePanel.java @@ -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 keyPaths) { - List nodes = new ArrayList<>(); + public void setSelectedKeyPaths(Collection keyPaths, EventOrigin origin) { + List 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 keyPaths) { + setSelectedKeyPaths(keyPaths, EventOrigin.API_GENERATED); } } diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/PathTableModel.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/PathTableModel.java index 8e3e1ae898..3ce61438b1 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/PathTableModel.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/PathTableModel.java @@ -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 { /** Initialized in {@link #createTableColumnDescriptor()}, which precedes this. */ @@ -89,6 +89,12 @@ public class PathTableModel extends AbstractQueryTableModel { 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 { 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); diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/register/DebuggerRegistersProvider.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/register/DebuggerRegistersProvider.java index 80e38e6569..87e3645e7f 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/register/DebuggerRegistersProvider.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/register/DebuggerRegistersProvider.java @@ -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)); } } } diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/time/DebuggerTimeProvider.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/time/DebuggerTimeProvider.java index 97d173bd64..2c3b4c8090 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/time/DebuggerTimeProvider.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/time/DebuggerTimeProvider.java @@ -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); diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/editing/DebuggerStateEditingServicePlugin.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/editing/DebuggerStateEditingServicePlugin.java index a547cb4e4a..16ce1b4573 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/editing/DebuggerStateEditingServicePlugin.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/editing/DebuggerStateEditingServicePlugin.java @@ -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 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 diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/record/ObjectRecorder.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/record/ObjectRecorder.java index b9fc94d191..feee834cf0 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/record/ObjectRecorder.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/record/ObjectRecorder.java @@ -268,6 +268,9 @@ class ObjectRecorder { protected T getTargetFrameInterface(TraceThread thread, int frameLevel, Class targetObjectIf) { + if (thread == null) { + return null; + } TraceObject object = ((TraceObjectThread) thread).getObject(); PathMatcher matcher = object.getTargetSchema().searchFor(targetObjectIf, false); PathPattern pattern = matcher.getSingletonPattern(); diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/tracemgr/DebuggerTraceManagerServicePlugin.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/tracemgr/DebuggerTraceManagerServicePlugin.java index cf41855161..5e5c27fa40 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/tracemgr/DebuggerTraceManagerServicePlugin.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/tracemgr/DebuggerTraceManagerServicePlugin.java @@ -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 it = - trace.getThreadManager().getLiveThreads(time.getSnap()).iterator(); - // docs say eldest come first - if (it.hasNext()) { - thread = it.next(); - } - } - if (thread /*STILL!?*/ == null) { - Iterator 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)); } } diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/utils/DefaultTransactionCoalescer.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/utils/DefaultTransactionCoalescer.java index cc62ed155d..eded120b81 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/utils/DefaultTransactionCoalescer.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/utils/DefaultTransactionCoalescer.java @@ -74,6 +74,10 @@ public class DefaultTransactionCoalescer - * 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" + * + *

+ * 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 * diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/debug/flatapi/FlatDebuggerAPI.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/debug/flatapi/FlatDebuggerAPI.java index 4234bfcf62..a472e03a4f 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/debug/flatapi/FlatDebuggerAPI.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/debug/flatapi/FlatDebuggerAPI.java @@ -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)); } /** diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/AbstractGhidraHeadedDebuggerGUITest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/AbstractGhidraHeadedDebuggerGUITest.java index 8efff5afda..b01d02f7b1 100644 --- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/AbstractGhidraHeadedDebuggerGUITest.java +++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/AbstractGhidraHeadedDebuggerGUITest.java @@ -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; } diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingProviderTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingProviderTest.java index 45330e0c65..5ad0ee9f24 100644 --- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingProviderTest.java +++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingProviderTest.java @@ -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()); diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesProviderTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesProviderTest.java index 482b07bbb5..f5c7b90f59 100644 --- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesProviderTest.java +++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesProviderTest.java @@ -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()); diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/model/DebuggerModelProviderTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/model/DebuggerModelProviderTest.java index c73ca91c07..d38db9f7ca 100644 --- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/model/DebuggerModelProviderTest.java +++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/model/DebuggerModelProviderTest.java @@ -27,6 +27,7 @@ import com.google.common.collect.Range; import docking.widgets.table.DynamicTableColumn; import docking.widgets.table.GDynamicColumnTableModel; +import docking.widgets.tree.support.GTreeSelectionEvent.EventOrigin; import generic.Unique; import ghidra.app.plugin.core.debug.DebuggerCoordinates; import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest; @@ -40,10 +41,10 @@ import ghidra.dbg.target.TargetObject; import ghidra.dbg.target.schema.SchemaContext; import ghidra.dbg.target.schema.TargetObjectSchema.SchemaName; import ghidra.dbg.target.schema.XmlSchemaContext; -import ghidra.trace.database.target.DBTraceObject; -import ghidra.trace.database.target.DBTraceObjectManager; import ghidra.trace.model.target.*; import ghidra.trace.model.target.TraceObject.ConflictResolution; +import ghidra.trace.model.thread.TraceObjectThread; +import ghidra.trace.model.thread.TraceThread; import ghidra.util.database.UndoableTransaction; public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerGUITest { @@ -52,34 +53,45 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerGUITe static { try { - CTX = XmlSchemaContext.deserialize("" + // - "" + // - " " + // - " " + // - " " + // - " " + // - " " + // - " " + // - " " + // - " " + // - " " + // - " " + // - " " + // - " " + // - " " + // - " " + // - " " + // - " " + // - " " + // - " " + // - " " + // - " " + // - " " + // - " " + // - ""); + CTX = XmlSchemaContext.deserialize( + """ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """); } catch (JDOMException e) { throw new AssertionError(); @@ -104,9 +116,6 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerGUITe public void setUpModelProviderTest() throws Exception { modelPlugin = addPlugin(tool, DebuggerModelPlugin.class); modelProvider = waitForComponentProvider(DebuggerModelProvider.class); - - // So I can manipulate the coordinates - //addPlugin(tool, DebuggerThreadsPlugin.class); } @After @@ -126,16 +135,16 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerGUITe } protected TraceObjectValue createSessionObject() throws Throwable { - DBTraceObjectManager objects = tb.trace.getObjectManager(); + TraceObjectManager objects = tb.trace.getObjectManager(); try (UndoableTransaction tid = tb.startTransaction()) { return objects.createRootObject(CTX.getSchema(new SchemaName("Session"))); } } - protected DBTraceObject createThread(long i, DBTraceObject prevThread) { - DBTraceObjectManager objects = tb.trace.getObjectManager(); + protected TraceObject createThread(long i, TraceObject prevThread) { + TraceObjectManager objects = tb.trace.getObjectManager(); TraceObjectKeyPath threadContainerPath = TraceObjectKeyPath.parse("Processes[0].Threads"); - DBTraceObject thread = objects.createObject(threadContainerPath.index(i)); + TraceObject thread = objects.createObject(threadContainerPath.index(i)); thread.insert(Range.closed(i, 10L), ConflictResolution.DENY); thread.insert(Range.atLeast(10 + i), ConflictResolution.DENY); thread.setAttribute(Range.atLeast(i), "Attribute " + i, "Some value"); @@ -151,18 +160,31 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerGUITe return thread; } + protected TraceObject createStack(TraceObject thread) { + try (UndoableTransaction tid = tb.startTransaction()) { + TraceObjectKeyPath stackPath = thread.getCanonicalPath().key("Stack"); + TraceObjectManager objects = tb.trace.getObjectManager(); + TraceObject stack = objects.createObject(stackPath); + objects.createObject(stackPath.index(0)) + .insert(thread.getLife().span(), ConflictResolution.TRUNCATE); + objects.createObject(stackPath.index(1)) + .insert(thread.getLife().span(), ConflictResolution.TRUNCATE); + return stack; + } + } + protected void populateThreads() throws Throwable { try (UndoableTransaction tid = tb.startTransaction()) { - DBTraceObject prevThread = null; + TraceObject prevThread = null; for (long i = 0; i < 10; i++) { - DBTraceObject thread = createThread(i, prevThread); + TraceObject thread = createThread(i, prevThread); prevThread = thread; } } } protected void addThread10() throws Throwable { - DBTraceObjectManager objects = tb.trace.getObjectManager(); + TraceObjectManager objects = tb.trace.getObjectManager(); try (UndoableTransaction tid = tb.startTransaction()) { createThread(10, objects.getObjectByCanonicalPath( TraceObjectKeyPath.parse("Processes[0].Threads[9]"))); @@ -170,9 +192,9 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerGUITe } protected void populateHandles() throws Throwable { - DBTraceObjectManager objects = tb.trace.getObjectManager(); + TraceObjectManager objects = tb.trace.getObjectManager(); try (UndoableTransaction tid = tb.startTransaction()) { - DBTraceObject handleContainer = + TraceObject handleContainer = objects.createObject(TraceObjectKeyPath.parse("Processes[0].Handles")); handleContainer.insert(Range.atLeast(0L), ConflictResolution.DENY); for (int i = 0; i < 10; i++) { @@ -183,10 +205,10 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerGUITe } protected void populateLinks() throws Throwable { - DBTraceObjectManager objects = tb.trace.getObjectManager(); + TraceObjectManager objects = tb.trace.getObjectManager(); TraceObjectKeyPath threadContainerPath = TraceObjectKeyPath.parse("Processes[0].Threads"); try (UndoableTransaction tid = tb.startTransaction()) { - DBTraceObject linkContainer = + TraceObject linkContainer = objects.createObject(TraceObjectKeyPath.parse("Processes[0].Links")); linkContainer.insert(Range.atLeast(0L), ConflictResolution.DENY); for (int i = 0; i < 10; i++) { @@ -197,7 +219,7 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerGUITe } protected void populateBoxedPrimitive() throws Throwable { - DBTraceObjectManager objects = tb.trace.getObjectManager(); + TraceObjectManager objects = tb.trace.getObjectManager(); try (UndoableTransaction tid = tb.startTransaction()) { TraceObject boxed = objects.createObject(TraceObjectKeyPath.parse("Processes[0].Boxed")); @@ -291,7 +313,7 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerGUITe createTraceAndPopulateObjects(); traceManager.activateTrace(tb.trace); - waitForSwing(); + waitForTasks(); modelProvider.objectsTreePanel .setSelectedKeyPaths(List.of(TraceObjectKeyPath.parse("Processes[0].Threads"))); waitForSwing(); @@ -306,7 +328,7 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerGUITe traceManager.activateTrace(tb.trace); waitForSwing(); modelProvider.setPath(TraceObjectKeyPath.parse("Processes[0].Threads")); - waitForSwing(); + waitForTasks(); ValueRow selElem = waitForValue(() -> { List rows = modelProvider.elementsTablePanel.tableModel.getModelData(); @@ -316,7 +338,7 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerGUITe return rows.get(2); }); modelProvider.elementsTablePanel.setSelectedItem(selElem); - waitForSwing(); + waitForTasks(); waitForPass(() -> assertEquals(3, modelProvider.attributesTablePanel.tableModel.getModelData().size())); @@ -329,7 +351,7 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerGUITe traceManager.activateTrace(tb.trace); waitForSwing(); modelProvider.setPath(TraceObjectKeyPath.parse("Processes[0].NoSuch")); - waitForSwing(); + waitForTasks(); assertEquals("No such object at path Processes[0].NoSuch", tool.getStatusInfo()); } @@ -341,7 +363,7 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerGUITe traceManager.activateTrace(tb.trace); waitForSwing(); modelProvider.setPath(TraceObjectKeyPath.parse("Processes[0].Handles")); - waitForSwing(); + waitForTasks(); int valColIndex = waitForValue(() -> findColumnOfClass(modelProvider.elementsTablePanel.tableModel, @@ -364,7 +386,7 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerGUITe traceManager.activateTrace(tb.trace); waitForSwing(); modelProvider.setPath(TraceObjectKeyPath.parse("Processes[0].Threads")); - waitForSwing(); + waitForTasks(); modelProvider.pathField.setText("SomeNonsenseToBeCancelled"); triggerEscapeKey(modelProvider.pathField); @@ -380,7 +402,7 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerGUITe traceManager.activateTrace(tb.trace); waitForSwing(); modelProvider.setPath(TraceObjectKeyPath.parse("Processes[0].Links")); - waitForSwing(); + waitForTasks(); ValueRow row2 = waitForValue(() -> { return modelProvider.elementsTablePanel.tableModel.getModelData() @@ -390,7 +412,7 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerGUITe .orElse(null); }); modelProvider.elementsTablePanel.setSelectedItem(row2); - waitForSwing(); + waitForTasks(); int rowIndex = waitForValue(() -> { int index = modelProvider.elementsTablePanel.table.getSelectedRow(); if (index == -1) { @@ -410,7 +432,7 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerGUITe traceManager.activateTrace(tb.trace); waitForSwing(); modelProvider.setPath(TraceObjectKeyPath.parse("Processes[0].Threads")); - waitForSwing(); + waitForTasks(); ValueRow row2 = waitForValue(() -> { return modelProvider.elementsTablePanel.tableModel.getModelData() @@ -420,7 +442,7 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerGUITe .orElse(null); }); modelProvider.elementsTablePanel.setSelectedItem(row2); - waitForSwing(); + waitForTasks(); int rowIndex = waitForValue(() -> { int index = modelProvider.elementsTablePanel.table.getSelectedRow(); if (index == -1) { @@ -458,9 +480,9 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerGUITe traceManager.activateTrace(tb.trace); waitForSwing(); modelProvider.setPath(TraceObjectKeyPath.parse("Processes[0].Threads[2]")); - waitForSwing(); + waitForTasks(); selectAttribute("_next"); - waitForSwing(); + waitForTasks(); int rowIndex = waitForValue(() -> { int index = modelProvider.attributesTablePanel.table.getSelectedRow(); @@ -481,7 +503,7 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerGUITe traceManager.activateTrace(tb.trace); waitForSwing(); modelProvider.setPath(TraceObjectKeyPath.parse("Processes[0]")); - waitForSwing(); + waitForTasks(); PathRow rowNext = waitForValue(() -> { return modelProvider.attributesTablePanel.tableModel.getModelData() @@ -497,7 +519,7 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerGUITe .orElse(null); }); modelProvider.attributesTablePanel.setSelectedItem(rowNext); - waitForSwing(); + waitForTasks(); int rowIndex = waitForValue(() -> { int index = modelProvider.attributesTablePanel.table.getSelectedRow(); if (index == -1) { @@ -519,7 +541,7 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerGUITe traceManager.activateTrace(tb.trace); waitForSwing(); modelProvider.setPath(TraceObjectKeyPath.parse("Processes[0].Threads")); - waitForSwing(); + waitForTasks(); assertPathIs(TraceObjectKeyPath.parse("Processes[0].Threads"), 10, 0); @@ -545,7 +567,7 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerGUITe traceManager.activateTrace(tb.trace); waitForSwing(); modelProvider.setPath(TraceObjectKeyPath.parse("Processes[0].Threads[2]")); - waitForSwing(); + waitForTasks(); AbstractNode nodeThread2 = waitForValue(() -> modelProvider.objectsTreePanel.getSelectedItem()); @@ -573,14 +595,16 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerGUITe traceManager.activateTrace(tb.trace); waitForSwing(); modelProvider.setPath(TraceObjectKeyPath.parse("Processes[0].Threads[2]")); - waitForSwing(); + waitForTasks(); selectAttribute("_next"); waitForSwing(); assertEnabled(modelProvider, modelProvider.actionFollowLink); performAction(modelProvider.actionFollowLink, modelProvider, true); - assertPathIs(TraceObjectKeyPath.parse("Processes[0].Threads[3]"), 0, 5); + TraceObjectKeyPath thread3Path = TraceObjectKeyPath.parse("Processes[0].Threads[3]"); + assertPathIs(thread3Path, 0, 5); + assertEquals(thread3Path, traceManager.getCurrentObject().getCanonicalPath()); } @Test @@ -590,7 +614,7 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerGUITe traceManager.activateTrace(tb.trace); waitForSwing(); modelProvider.setPath(TraceObjectKeyPath.parse("Processes[0].Threads[2]")); - waitForSwing(); + waitForTasks(); performAction(modelProvider.actionCloneWindow); @@ -608,12 +632,12 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerGUITe traceManager.activateTrace(tb.trace); waitForSwing(); modelProvider.setPath(path); - waitForSwing(); + waitForTasks(); assertPathIsThreadsContainer(); addThread10(); - waitForSwing(); + waitForTasks(); assertPathIs(path, 11, 0); } @@ -626,15 +650,15 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerGUITe traceManager.activateTrace(tb.trace); waitForSwing(); modelProvider.setPath(path); - waitForSwing(); + waitForTasks(); assertPathIs(path, 0, 3); try (UndoableTransaction tid = tb.startTransaction()) { - DBTraceObject thread = tb.trace.getObjectManager().getObjectByCanonicalPath(path); + TraceObject thread = tb.trace.getObjectManager().getObjectByCanonicalPath(path); thread.setAttribute(Range.atLeast(0L), "NewAttribute", 11); } - waitForSwing(); + waitForTasks(); assertPathIs(path, 0, 4); } @@ -647,15 +671,15 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerGUITe traceManager.activateTrace(tb.trace); waitForSwing(); modelProvider.setPath(path); - waitForSwing(); + waitForTasks(); assertPathIsThreadsContainer(); try (UndoableTransaction tid = tb.startTransaction()) { - DBTraceObject threads = tb.trace.getObjectManager().getObjectByCanonicalPath(path); + TraceObject threads = tb.trace.getObjectManager().getObjectByCanonicalPath(path); threads.setElement(Range.all(), 2, null); } - waitForSwing(); + waitForTasks(); assertPathIs(path, 9, 0); } @@ -668,15 +692,15 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerGUITe traceManager.activateTrace(tb.trace); waitForSwing(); modelProvider.setPath(path); - waitForSwing(); + waitForTasks(); assertPathIs(path, 0, 3); try (UndoableTransaction tid = tb.startTransaction()) { - DBTraceObject thread = tb.trace.getObjectManager().getObjectByCanonicalPath(path); + TraceObject thread = tb.trace.getObjectManager().getObjectByCanonicalPath(path); thread.setAttribute(Range.all(), "_self", null); } - waitForSwing(); + waitForTasks(); assertPathIs(path, 0, 2); } @@ -693,21 +717,21 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerGUITe traceManager.activateSnap(2); waitForSwing(); modelProvider.setPath(path); - waitForSwing(); + waitForTasks(); assertPathIs(path, 3, 0); try (UndoableTransaction tid = tb.startTransaction()) { element2.setLifespan(Range.atLeast(10L), ConflictResolution.DENY); } - waitForSwing(); + waitForTasks(); assertPathIs(path, 2, 0); try (UndoableTransaction tid = tb.startTransaction()) { element2.setLifespan(Range.atLeast(2L), ConflictResolution.DENY); } - waitForSwing(); + waitForTasks(); assertPathIs(path, 3, 0); } @@ -725,21 +749,21 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerGUITe traceManager.activateSnap(2); waitForSwing(); modelProvider.setPath(path); - waitForSwing(); + waitForTasks(); assertPathIs(path, 0, 4); // _next created at snap 3 try (UndoableTransaction tid = tb.startTransaction()) { attrSelf.setLifespan(Range.atLeast(10L), ConflictResolution.DENY); } - waitForSwing(); + waitForTasks(); assertPathIs(path, 0, 3); try (UndoableTransaction tid = tb.startTransaction()) { attrSelf.setLifespan(Range.atLeast(2L), ConflictResolution.DENY); } - waitForSwing(); + waitForTasks(); assertPathIs(path, 0, 4); } @@ -753,7 +777,7 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerGUITe traceManager.activateTrace(tb.trace); waitForSwing(); modelProvider.setPath(path); - waitForSwing(); + waitForTasks(); AbstractNode node = waitForValue(() -> modelProvider.objectsTreePanel.treeModel.getNode(path)); @@ -762,8 +786,253 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerGUITe try (UndoableTransaction tid = tb.startTransaction()) { thread.setAttribute(Range.atLeast(0L), "_display", "Renamed Thread"); } - waitForSwing(); + waitForTasks(); waitForPass(() -> assertEquals("Renamed Thread", node.getDisplayText())); } + + @Test + public void testTreeSelectionActivatesObject() throws Throwable { + createTraceAndPopulateObjects(); + + TraceObjectManager objects = tb.trace.getObjectManager(); + TraceObject root = objects.getRootObject(); + TraceObjectKeyPath processesPath = TraceObjectKeyPath.parse("Processes"); + TraceObject processes = objects.getObjectByCanonicalPath(processesPath); + traceManager.activateObject(root); + waitForTasks(); + + modelProvider.setTreeSelection(processesPath, EventOrigin.USER_GENERATED); + waitForSwing(); + assertEquals(processes, traceManager.getCurrentObject()); + } + + @Test + public void testElementSelectionActivatesObject() throws Throwable { + createTraceAndPopulateObjects(); + TraceObjectManager objects = tb.trace.getObjectManager(); + TraceObject processes = + objects.getObjectByCanonicalPath(TraceObjectKeyPath.parse("Processes")); + TraceObject process0 = processes.getElement(0, 0).getChild(); + traceManager.activateObject(processes); + waitForTasks(); + + assertTrue(modelProvider.elementsTablePanel.trySelect(process0)); + waitForSwing(); + assertEquals(process0, traceManager.getCurrentObject()); + } + + @Test + public void testAttributeSelectionActivatesObject() throws Throwable { + createTraceAndPopulateObjects(); + + TraceObjectManager objects = tb.trace.getObjectManager(); + TraceObject root = objects.getRootObject(); + TraceObject processes = root.getAttribute(0, "Processes").getChild(); + traceManager.activateObject(root); + waitForTasks(); + + assertTrue(modelProvider.attributesTablePanel.trySelect(processes)); + waitForSwing(); + assertEquals(processes, traceManager.getCurrentObject()); + } + + @Test + public void testObjectActivationSelectsTree() throws Throwable { + createTraceAndPopulateObjects(); + TraceObjectManager objects = tb.trace.getObjectManager(); + TraceObject root = objects.getRootObject(); + TraceObject process0 = + objects.getObjectByCanonicalPath(TraceObjectKeyPath.parse("Processes[0]")); + + traceManager.activateObject(root); + waitForTasks(); + assertEquals(root, modelProvider.getTreeSelection().getChild()); + + /** + * NOTE: Have to skip a level, lest is select the child in the attributes pane instead + */ + traceManager.activateObject(process0); + waitForTasks(); + assertEquals(process0, modelProvider.getTreeSelection().getChild()); + } + + @Test + public void testObjectActivationParentDoesNothing() throws Throwable { + createTraceAndPopulateObjects(); + TraceObjectManager objects = tb.trace.getObjectManager(); + TraceObject root = objects.getRootObject(); + TraceObject processes = root.getAttribute(0, "Processes").getChild(); + + traceManager.activateObject(processes); + waitForTasks(); + assertEquals(processes, modelProvider.getTreeSelection().getChild()); + + // TODO: Is this the desired behavior? + traceManager.activateObject(root); + waitForTasks(); + assertEquals(processes, modelProvider.getTreeSelection().getChild()); + } + + @Test + public void testObjectActivationSiblingSelectsTree() throws Throwable { + createTraceAndPopulateObjects(); + TraceObjectManager objects = tb.trace.getObjectManager(); + TraceObject thread0 = + objects.getObjectByCanonicalPath(TraceObjectKeyPath.parse("Processes[0].Threads[0]")); + TraceObject thread1 = + objects.getObjectByCanonicalPath(TraceObjectKeyPath.parse("Processes[0].Threads[1]")); + + modelProvider.setShowHidden(true); + traceManager.activateObject(thread0); + traceManager.activateSnap(1); + waitForTasks(); + modelProvider.setPath(TraceObjectKeyPath.parse("Processes[0].Threads[0]._self")); + waitForTasks(); + + traceManager.activateObject(thread1); + waitForSwing(); + assertEquals(TraceObjectKeyPath.parse("Processes[0].Threads[0]._next"), + modelProvider.getPath()); + } + + @Test + public void testObjectActivationSelectsElement() throws Throwable { + createTraceAndPopulateObjects(); + TraceObjectManager objects = tb.trace.getObjectManager(); + TraceObjectKeyPath processesPath = TraceObjectKeyPath.parse("Processes"); + TraceObject processes = objects.getObjectByCanonicalPath(processesPath); + TraceObject process0 = processes.getElement(0, 0).getChild(); + traceManager.activateObject(processes); + waitForTasks(); + + /** + * TODO: It's interesting that activating a parent then a child produces a different end + * result than activating the child directly. + */ + traceManager.activateObject(process0); + waitForTasks(); + + assertEquals(processesPath, modelProvider.getPath()); + assertEquals(process0, + modelProvider.elementsTablePanel.getSelectedItem().getValue().getChild()); + } + + @Test + public void testObjectActivationSelectsAttribute() throws Throwable { + createTraceAndPopulateObjects(); + TraceObjectManager objects = tb.trace.getObjectManager(); + TraceObject root = objects.getRootObject(); + TraceObject processes = root.getAttribute(0, "Processes").getChild(); + traceManager.activateObject(root); + waitForTasks(); + + /** + * TODO: It's interesting that activating a parent then a child produces a different end + * result than activating the child directly. + */ + traceManager.activateObject(processes); + waitForTasks(); + + assertEquals(TraceObjectKeyPath.of(), modelProvider.getPath()); + assertEquals(processes, modelProvider.attributesTablePanel.getSelectedItem().getValue()); + } + + protected TraceThread populateThread0Stack() { + TraceObjectManager objects = tb.trace.getObjectManager(); + TraceObject threadObj0 = + objects.getObjectByCanonicalPath(TraceObjectKeyPath.parse("Processes[0].Threads[0]")); + TraceThread thread0 = threadObj0.queryInterface(TraceObjectThread.class); + createStack(threadObj0); + return thread0; + } + + @Test + public void testFrameActivationSelectsSibling() throws Throwable { + createTraceAndPopulateObjects(); + TraceThread thread0 = populateThread0Stack(); + + traceManager.activate(DebuggerCoordinates.NOWHERE.thread(thread0).frame(0)); + waitForSwing(); + assertEquals(TraceObjectKeyPath.parse("Processes[0].Threads[0].Stack[0]"), + modelProvider.getPath()); + + traceManager.activateFrame(1); + waitForSwing(); + assertEquals(TraceObjectKeyPath.parse("Processes[0].Threads[0].Stack[1]"), + modelProvider.getPath()); + } + + @Test + public void testFrameActivationSelectsElement() throws Throwable { + createTraceAndPopulateObjects(); + TraceThread thread0 = populateThread0Stack(); + TraceObjectKeyPath stackPath = TraceObjectKeyPath.parse("Processes[0].Threads[0].Stack"); + + traceManager.activateThread(thread0); + waitForSwing(); + modelProvider.setPath(stackPath); + waitForTasks(); + + // Test 1 then 0, because 0 is default + traceManager.activateFrame(1); + waitForTasks(); + assertEquals(stackPath, modelProvider.getPath()); + assertEquals(stackPath.index(1), + modelProvider.elementsTablePanel.getSelectedItem().getValue().getCanonicalPath()); + + traceManager.activateFrame(0); + waitForTasks(); + assertEquals(stackPath, modelProvider.getPath()); + assertEquals(stackPath.index(0), + modelProvider.elementsTablePanel.getSelectedItem().getValue().getCanonicalPath()); + } + + @Test + public void testThreadActivationSelectsSibling() throws Throwable { + createTraceAndPopulateObjects(); + TraceThread thread0 = + tb.trace.getThreadManager().getLiveThreadByPath(1, "Processes[0].Threads[0]"); + TraceThread thread1 = + tb.trace.getThreadManager().getLiveThreadByPath(1, "Processes[0].Threads[1]"); + + traceManager.activateThread(thread0); + traceManager.activateSnap(1); + waitForSwing(); + assertEquals(TraceObjectKeyPath.parse("Processes[0].Threads[0]"), modelProvider.getPath()); + + traceManager.activateThread(thread1); + waitForSwing(); + assertEquals(TraceObjectKeyPath.parse("Processes[0].Threads[1]"), modelProvider.getPath()); + } + + @Test + public void testThreadActivationSelectsElement() throws Throwable { + createTraceAndPopulateObjects(); + TraceThread thread0 = + tb.trace.getThreadManager().getLiveThreadByPath(1, "Processes[0].Threads[0]"); + TraceThread thread1 = + tb.trace.getThreadManager().getLiveThreadByPath(1, "Processes[0].Threads[1]"); + TraceObjectKeyPath threadsPath = TraceObjectKeyPath.parse("Processes[0].Threads"); + + traceManager.activateTrace(tb.trace); + traceManager.activateSnap(1); + waitForSwing(); + + modelProvider.setPath(threadsPath); + waitForTasks(); + + // Testing 1 then 0, because 0 is default + traceManager.activateThread(thread1); + waitForSwing(); + assertEquals(threadsPath, modelProvider.getPath()); + assertEquals(threadsPath.index(1), + modelProvider.elementsTablePanel.getSelectedItem().getValue().getCanonicalPath()); + + traceManager.activateThread(thread0); + waitForSwing(); + assertEquals(threadsPath, modelProvider.getPath()); + assertEquals(threadsPath.index(0), + modelProvider.elementsTablePanel.getSelectedItem().getValue().getCanonicalPath()); + } } diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerStaticMappingProviderTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerStaticMappingProviderTest.java index b9bf7ea828..a94d6c3550 100644 --- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerStaticMappingProviderTest.java +++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerStaticMappingProviderTest.java @@ -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 diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/register/DebuggerRegistersProviderTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/register/DebuggerRegistersProviderTest.java index 1bddd5c7a9..ef189443b4 100644 --- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/register/DebuggerRegistersProviderTest.java +++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/register/DebuggerRegistersProviderTest.java @@ -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); diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/watch/DebuggerWatchesProviderTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/watch/DebuggerWatchesProviderTest.java index 326709d880..cf8c933d51 100644 --- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/watch/DebuggerWatchesProviderTest.java +++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/watch/DebuggerWatchesProviderTest.java @@ -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(() -> { diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/breakpoint/DebuggerLogicalBreakpointServiceTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/breakpoint/DebuggerLogicalBreakpointServiceTest.java index d94396334b..42f67676a0 100644 --- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/breakpoint/DebuggerLogicalBreakpointServiceTest.java +++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/breakpoint/DebuggerLogicalBreakpointServiceTest.java @@ -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()); diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/editing/DebuggerStateEditingServiceTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/editing/DebuggerStateEditingServiceTest.java index 7ac389202c..4baf378161 100644 --- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/editing/DebuggerStateEditingServiceTest.java +++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/editing/DebuggerStateEditingServiceTest.java @@ -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 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 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)); diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/tracemgr/DebuggerTraceManagerServiceTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/tracemgr/DebuggerTraceManagerServiceTest.java index abf3ff2d30..e5fd7d20f3 100644 --- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/tracemgr/DebuggerTraceManagerServiceTest.java +++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/tracemgr/DebuggerTraceManagerServiceTest.java @@ -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 diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/debug/flatapi/FlatDebuggerAPITest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/debug/flatapi/FlatDebuggerAPITest.java index 8e9f8bec68..b4ea9a3ab4 100644 --- a/Ghidra/Debug/Debugger/src/test/java/ghidra/debug/flatapi/FlatDebuggerAPITest.java +++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/debug/flatapi/FlatDebuggerAPITest.java @@ -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 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 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")); diff --git a/Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/model/TestTargetSession.java b/Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/model/TestTargetSession.java index 9fdded43dd..0e43681f71 100644 --- a/Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/model/TestTargetSession.java +++ b/Ghidra/Debug/Framework-Debugging/src/test/java/ghidra/dbg/model/TestTargetSession.java @@ -68,8 +68,8 @@ public class TestTargetSession extends DefaultTargetModelRoot @Override public CompletableFuture 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"); })); } diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/stack/DBTraceObjectStack.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/stack/DBTraceObjectStack.java index a4c981d0aa..d8c2743de6 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/stack/DBTraceObjectStack.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/stack/DBTraceObjectStack.java @@ -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); } } diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/target/DBTraceObjectValue.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/target/DBTraceObjectValue.java index af0984c8b8..517a882aa5 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/target/DBTraceObjectValue.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/target/DBTraceObjectValue.java @@ -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); }