From b5d888556760dcb8deb039fa445003e745c98dea Mon Sep 17 00:00:00 2001 From: Dan <46821332+nsadeveloper789@users.noreply.github.com> Date: Fri, 3 Jan 2025 13:43:34 -0500 Subject: [PATCH] GP-5229: Add failing test. Fix tree attribute update. --- .../core/debug/gui/model/ObjectTreeModel.java | 8 ++++ .../gui/model/DebuggerModelProviderTest.java | 48 +++++++++++++++++-- .../trace/database/ToyDBTraceBuilder.java | 26 +++++++++- 3 files changed, 77 insertions(+), 5 deletions(-) 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 cca4e5aa38..62f6d82f68 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 @@ -340,8 +340,16 @@ public class ObjectTreeModel implements DisplaysModified { while (ic < current.size() && ig < generated.size()) { GTreeNode nc = current.get(ic); GTreeNode ng = generated.get(ig); + if (nc == ng) { + ic++; + ig++; + continue; + } int comp = nc.compareTo(ng); if (comp == 0) { + // Same path, but not identical. Replace. + addNode(ic + diff, ng); + removeNode(nc); ic++; ig++; } 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 fc2c8b8ed2..bf85decb18 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 @@ -39,6 +39,7 @@ import ghidra.app.plugin.core.debug.gui.model.ObjectTreeModel.AbstractNode; import ghidra.app.plugin.core.debug.gui.model.PathTableModel.PathRow; import ghidra.app.plugin.core.debug.gui.model.columns.*; import ghidra.debug.api.tracemgr.DebuggerCoordinates; +import ghidra.trace.database.ToyDBTraceBuilder.EventSuspension; import ghidra.trace.model.Lifespan; import ghidra.trace.model.target.*; import ghidra.trace.model.target.TraceObject.ConflictResolution; @@ -46,8 +47,8 @@ import ghidra.trace.model.target.iface.TraceObjectEventScope; import ghidra.trace.model.target.iface.TraceObjectInterface; import ghidra.trace.model.target.path.KeyPath; import ghidra.trace.model.target.schema.SchemaContext; -import ghidra.trace.model.target.schema.XmlSchemaContext; import ghidra.trace.model.target.schema.TraceObjectSchema.SchemaName; +import ghidra.trace.model.target.schema.XmlSchemaContext; import ghidra.trace.model.thread.TraceObjectThread; import ghidra.trace.model.thread.TraceThread; @@ -694,6 +695,47 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerTest assertPathIs(path, 11, 0); } + /** + * The trace-rmi handler suspends trace events during a remote transaction. Also, if there are + * sufficient events to overload the queue, the event support with clear them and just issue an + * OBJ_RESTORED event instead. This test ensures we update attributes in the tree when that + * happens. + */ + @Test + public void testTreeTracksChangeAttributeWithEventsSuspended() throws Throwable { + createTraceAndPopulateObjects(); + KeyPath path = KeyPath.parse("Processes[0].Threads[2].Bytes"); + try (Transaction tx = tb.startTransaction()) { + TraceObject thread = + tb.trace.getObjectManager().getObjectByCanonicalPath(path.parent()); + thread.setAttribute(Lifespan.nowOn(0), "Bytes", tb.arr(1, 2, 3, 4)); + } + + traceManager.activateTrace(tb.trace); + waitForSwing(); + runSwing(() -> modelProvider.setShowPrimitivesInTree(true)); + waitForTasks(); + modelProvider.setPath(path); + waitForTasks(); + + waitForPass(() -> { + AbstractNode node = modelProvider.objectsTreePanel.treeModel.getNode(path); + assertEquals("Bytes: 01:02:03:04", node.getDisplayText()); + }); + + try (Transaction tx = tb.startTransaction(); EventSuspension es = tb.suspendEvents()) { + TraceObject thread = + tb.trace.getObjectManager().getObjectByCanonicalPath(path.parent()); + thread.setAttribute(Lifespan.nowOn(0), "Bytes", tb.arr(5, 6, 7, 8)); + } + waitForTasks(); + + waitForPass(() -> { + AbstractNode node = modelProvider.objectsTreePanel.treeModel.getNode(path); + assertEquals("Bytes: 05:06:07:08", node.getDisplayText()); + }); + } + @Test public void testPanesTrackAddAttribute() throws Throwable { createTraceAndPopulateObjects(); @@ -1239,11 +1281,9 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerTest traceManager.activateTrace(tb.trace); waitForSwing(); - tb.trace.setEventsEnabled(false); - try (Transaction tx = tb.startTransaction()) { + try (Transaction tx = tb.startTransaction(); EventSuspension es = tb.suspendEvents()) { tb.trace.getTimeManager().getSnapshot(30, true); } - tb.trace.setEventsEnabled(true); waitForDomainObject(tb.trace); // NB. The plot adds a margin of 1 diff --git a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/ToyDBTraceBuilder.java b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/ToyDBTraceBuilder.java index ce51174981..94d43a0e9e 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/ToyDBTraceBuilder.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/test/java/ghidra/trace/database/ToyDBTraceBuilder.java @@ -56,8 +56,9 @@ import ghidra.trace.model.*; import ghidra.trace.model.guest.TraceGuestPlatform; import ghidra.trace.model.guest.TracePlatform; import ghidra.trace.model.symbol.TraceReferenceManager; -import ghidra.trace.model.target.*; +import ghidra.trace.model.target.TraceObject; import ghidra.trace.model.target.TraceObject.ConflictResolution; +import ghidra.trace.model.target.TraceObjectValue; import ghidra.trace.model.target.path.KeyPath; import ghidra.trace.model.target.path.PathFilter; import ghidra.trace.model.thread.TraceObjectThread; @@ -432,6 +433,17 @@ public class ToyDBTraceBuilder implements AutoCloseable { return result.flip(); } + public class EventSuspension implements AutoCloseable { + public EventSuspension() { + trace.setEventsEnabled(false); + } + + @Override + public void close() { + trace.setEventsEnabled(true); + } + } + /** * Start a transaction on the trace * @@ -444,6 +456,18 @@ public class ToyDBTraceBuilder implements AutoCloseable { return trace.openTransaction("Testing"); } + /** + * Suspend events for the trace + * + *
+ * Use this in a {@code try-with-resources} block + * + * @return the suspension handle + */ + public EventSuspension suspendEvents() { + return new EventSuspension(); + } + /** * Ensure the given bookmark type exists and retrieve it *