diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointMarkerPlugin.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointMarkerPlugin.java index 746c280aa6..bf24ac7145 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointMarkerPlugin.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointMarkerPlugin.java @@ -969,7 +969,7 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin */ Trace trace = getTraceFromContext(context); boolean mapped = breakpointService.anyMapped(bs, trace); - Enablement toggled = en.getToggled(mapped); + Enablement toggled = en.getToggled(mapped && trace == null); if (toggled.enabled) { breakpointService.enableAll(bs, trace).exceptionally(ex -> { breakpointError(title, "Could not enable breakpoints", ex); diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/pcode/DebuggerPcodeStepperProvider.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/pcode/DebuggerPcodeStepperProvider.java index a447bd81cc..a3239e19ae 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/pcode/DebuggerPcodeStepperProvider.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/pcode/DebuggerPcodeStepperProvider.java @@ -16,6 +16,8 @@ package ghidra.app.plugin.core.debug.gui.pcode; import java.awt.*; +import java.awt.font.FontRenderContext; +import java.awt.geom.AffineTransform; import java.math.BigInteger; import java.util.*; import java.util.List; @@ -65,6 +67,8 @@ import ghidra.util.table.GhidraTableFilterPanel; import ghidra.util.table.column.AbstractGColumnRenderer; public class DebuggerPcodeStepperProvider extends ComponentProviderAdapter { + private static final FontRenderContext METRIC_FRC = + new FontRenderContext(new AffineTransform(), false, false); private static final String BACKGROUND_COLOR = "Background Color"; private static final String ADDRESS_COLOR = "Address Color"; private static final String CONSTANT_COLOR = "Constant Color"; @@ -458,6 +462,12 @@ public class DebuggerPcodeStepperProvider extends ComponentProviderAdapter { pcodeTableModel.fireTableDataChanged(); } + protected int computeSeqColWidth(JLabel renderer) { + Font font = renderer.getFont(); + Insets insets = renderer.getBorder().getBorderInsets(renderer); + return (int) font.getStringBounds("00", METRIC_FRC).getWidth() + insets.left + insets.right; + } + protected void buildMainPanel() { JPanel pcodePanel = new JPanel(new BorderLayout()); pcodeTable = new GhidraTable(pcodeTableModel); @@ -486,9 +496,11 @@ public class DebuggerPcodeStepperProvider extends ComponentProviderAdapter { TableColumnModel pcodeColModel = pcodeTable.getColumnModel(); TableColumn seqCol = pcodeColModel.getColumn(PcodeTableColumns.SEQUENCE.ordinal()); - seqCol.setCellRenderer(new CounterBackgroundCellRenderer()); - seqCol.setMinWidth(24); - seqCol.setMaxWidth(24); + CounterBackgroundCellRenderer seqColRenderer = new CounterBackgroundCellRenderer(); + seqCol.setCellRenderer(seqColRenderer); + int seqColWidth = computeSeqColWidth(seqColRenderer); + seqCol.setMinWidth(seqColWidth); + seqCol.setMaxWidth(seqColWidth); TableColumn codeCol = pcodeColModel.getColumn(PcodeTableColumns.CODE.ordinal()); codeCol.setCellRenderer(new PcodeCellRenderer()); //codeCol.setPreferredWidth(75); diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointMarkerPluginTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointMarkerPluginTest.java index 0498ad31d1..ccc24403b5 100644 --- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointMarkerPluginTest.java +++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointMarkerPluginTest.java @@ -499,6 +499,20 @@ public class DebuggerBreakpointMarkerPluginTest extends AbstractGhidraHeadedDebu dynamicCtx(trace, addr(trace, 0x55550123)), true); waitForPass(() -> assertEquals(Enablement.ENABLED, lb.computeEnablementForTrace(trace))); + + lb.disable(); + waitForPass(() -> assertEquals(Enablement.DISABLED, lb.computeEnablementForTrace(trace))); + + performAction(breakpointMarkerPlugin.actionToggleBreakpoint, + dynamicCtx(trace, addr(trace, 0x55550123)), true); + + waitForPass( + () -> assertEquals(Enablement.ENABLED_DISABLED, lb.computeEnablementForTrace(trace))); + + performAction(breakpointMarkerPlugin.actionToggleBreakpoint, + dynamicCtx(trace, addr(trace, 0x55550123)), true); + + waitForPass(() -> assertEquals(Enablement.DISABLED, lb.computeEnablementForTrace(trace))); } protected void testActionSetBreakpointProgram(DockingAction action, diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/memory/DBTraceMemorySpace.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/memory/DBTraceMemorySpace.java index ac89d9a5b9..1b88e672e7 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/memory/DBTraceMemorySpace.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/memory/DBTraceMemorySpace.java @@ -44,8 +44,8 @@ import ghidra.trace.model.*; import ghidra.trace.model.Trace.*; import ghidra.trace.model.memory.*; import ghidra.trace.model.thread.TraceThread; +import ghidra.trace.util.DefaultTraceTimeViewport; import ghidra.trace.util.TraceChangeRecord; -import ghidra.trace.util.TraceViewportSpanIterator; import ghidra.util.*; import ghidra.util.AddressIteratorAdapter; import ghidra.util.database.*; @@ -93,6 +93,8 @@ public class DBTraceMemorySpace implements Unfinished, TraceMemorySpace, DBTrace .build() .asMap(); + protected final DefaultTraceTimeViewport viewport; + public DBTraceMemorySpace(DBTraceMemoryManager manager, DBHandle dbh, AddressSpace space, DBTraceSpaceEntry ent) throws IOException, VersionException { this.manager = manager; @@ -127,6 +129,8 @@ public class DBTraceMemorySpace implements Unfinished, TraceMemorySpace, DBTrace true); this.blocksByOffset = blockStore.getIndex(OffsetSnap.class, DBTraceMemoryBlockEntry.LOCATION_COLUMN); + + this.viewport = new DefaultTraceTimeViewport(trace); } private void regionCacheEntryRemoved( @@ -375,9 +379,7 @@ public class DBTraceMemorySpace implements Unfinished, TraceMemorySpace, DBTrace @Override public Entry getViewState(long snap, Address address) { - TraceViewportSpanIterator spit = new TraceViewportSpanIterator(trace, snap); - while (spit.hasNext()) { - Range span = spit.next(); + for (Range span : viewport.getOrderedSpans(snap)) { TraceMemoryState state = getState(span.upperEndpoint(), address); switch (state) { case KNOWN: @@ -403,9 +405,7 @@ public class DBTraceMemorySpace implements Unfinished, TraceMemorySpace, DBTrace @Override public Entry getViewMostRecentStateEntry(long snap, Address address) { - TraceViewportSpanIterator spit = new TraceViewportSpanIterator(trace, snap); - while (spit.hasNext()) { - Range span = spit.next(); + for (Range span: viewport.getOrderedSpans(snap)) { Entry entry = stateMapSpace.reduce(TraceAddressSnapRangeQuery.mostRecent(address, span)) .firstEntry(); @@ -738,9 +738,8 @@ public class DBTraceMemorySpace implements Unfinished, TraceMemorySpace, DBTrace } Map sources = new TreeMap<>(); AddressSet remains = new AddressSet(toRead); - TraceViewportSpanIterator spit = new TraceViewportSpanIterator(trace, snap); - spans: while (spit.hasNext()) { - Range span = spit.next(); + + spans: for (Range span : viewport.getOrderedSpans(snap)) { Iterator arit = getAddressesWithState(span, s -> s == TraceMemoryState.KNOWN).iterator(start, true); while (arit.hasNext()) { @@ -765,7 +764,9 @@ public class DBTraceMemorySpace implements Unfinished, TraceMemorySpace, DBTrace int offset = (int) rng.getMinAddress().subtract(toRead.getMinAddress()); int length = (int) rng.getLength(); buf.limit(pos + offset + length); - buf.position(pos + offset); + while (buf.position() < pos + offset) { + buf.put((byte) 0); // fill gaps with 0 + } int read = getBytes(ent.getValue(), rng.getMinAddress(), buf); if (read < length) { break; @@ -773,7 +774,9 @@ public class DBTraceMemorySpace implements Unfinished, TraceMemorySpace, DBTrace } // We "got it all", even if there were gaps in "KNOWN" buf.limit(lim); - buf.position(pos + len); + while (buf.position() < pos + len) { + buf.put((byte) 0); // fill final gap with 0 + } return len; } diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/util/DefaultTraceTimeViewport.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/util/DefaultTraceTimeViewport.java index 4c18052092..67cb7bc0e8 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/util/DefaultTraceTimeViewport.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/util/DefaultTraceTimeViewport.java @@ -100,22 +100,20 @@ public class DefaultTraceTimeViewport implements TraceTimeViewport { } protected final Trace trace; - protected final TraceTimeManager timeManager; /** - * NB: This is the syncing object for the viewport. If there's even a chance an operation may - * need the DB's lock, esp., considering user callbacks, then it must first acquire the - * DB lock. + * NB: This is also the syncing object for the viewport. If there's even a chance an operation + * may need the DB's lock, esp., considering user callbacks, then it must first acquire + * the DB lock. */ - protected final List> ordered = new ArrayList<>(); + protected final List> ordered = new ArrayList<>(List.of(Range.singleton(0L))); protected final RangeSet spanSet = TreeRangeSet.create(); protected final ForSnapshotsListener listener = new ForSnapshotsListener(); protected final ListenerSet changeListeners = new ListenerSet<>(Runnable.class); - protected long snap; + protected long snap = 0; public DefaultTraceTimeViewport(Trace trace) { this.trace = trace; - this.timeManager = trace.getTimeManager(); trace.addCloseListener(listener); trace.addListener(listener); } @@ -266,7 +264,7 @@ public class DefaultTraceTimeViewport implements TraceTimeViewport { RangeSet spanSet = TreeRangeSet.create(); List> ordered = new ArrayList<>(); try (LockHold hold = trace.lockRead()) { - collectForkRanges(timeManager, snap, spanSet, ordered); + collectForkRanges(trace.getTimeManager(), snap, spanSet, ordered); } synchronized (this.ordered) { this.spanSet.clear(); @@ -279,6 +277,9 @@ public class DefaultTraceTimeViewport implements TraceTimeViewport { } public void setSnap(long snap) { + if (this.snap == snap) { + return; + } this.snap = snap; refreshSnapRanges(); } @@ -329,6 +330,19 @@ public class DefaultTraceTimeViewport implements TraceTimeViewport { } } + public List> getOrderedSpans() { + synchronized (ordered) { + return List.copyOf(ordered); + } + } + + public List> getOrderedSpans(long snap) { + synchronized (ordered) { + setSnap(snap); + return getOrderedSpans(); + } + } + @Override public List getOrderedSnaps() { synchronized (ordered) {