mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
GP-1008: Fixed debugger table issues.
This commit is contained in:
parent
10990723b1
commit
60a68e64e6
14 changed files with 154 additions and 61 deletions
|
@ -768,7 +768,6 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter
|
||||||
@Override
|
@Override
|
||||||
public void breakpointsRemoved(Collection<LogicalBreakpoint> clb) {
|
public void breakpointsRemoved(Collection<LogicalBreakpoint> clb) {
|
||||||
Swing.runIfSwingOrRunLater(() -> {
|
Swing.runIfSwingOrRunLater(() -> {
|
||||||
Msg.debug(this, "LBs removed: " + clb);
|
|
||||||
breakpointTableModel.deleteAllItems(clb);
|
breakpointTableModel.deleteAllItems(clb);
|
||||||
contextChanged();
|
contextChanged();
|
||||||
});
|
});
|
||||||
|
|
|
@ -34,6 +34,11 @@ public class LogicalBreakpointRow {
|
||||||
this.lb = lb;
|
this.lb = lb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "<Row " + lb + ">";
|
||||||
|
}
|
||||||
|
|
||||||
public LogicalBreakpoint getLogicalBreakpoint() {
|
public LogicalBreakpoint getLogicalBreakpoint() {
|
||||||
return lb;
|
return lb;
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,27 +52,26 @@ import ghidra.util.Swing;
|
||||||
import ghidra.util.datastruct.CollectionChangeListener;
|
import ghidra.util.datastruct.CollectionChangeListener;
|
||||||
import ghidra.util.datastruct.ListenerSet;
|
import ghidra.util.datastruct.ListenerSet;
|
||||||
|
|
||||||
@PluginInfo( //
|
@PluginInfo(
|
||||||
shortDescription = "Debugger logical breakpoints service plugin", //
|
shortDescription = "Debugger logical breakpoints service plugin",
|
||||||
description = "Aggregates breakpoints from open programs and live traces", //
|
description = "Aggregates breakpoints from open programs and live traces",
|
||||||
category = PluginCategoryNames.DEBUGGER, //
|
category = PluginCategoryNames.DEBUGGER,
|
||||||
packageName = DebuggerPluginPackage.NAME, //
|
packageName = DebuggerPluginPackage.NAME,
|
||||||
status = PluginStatus.RELEASED, //
|
status = PluginStatus.RELEASED,
|
||||||
eventsConsumed = { //
|
eventsConsumed = {
|
||||||
ProgramOpenedPluginEvent.class, //
|
ProgramOpenedPluginEvent.class,
|
||||||
ProgramClosedPluginEvent.class, //
|
ProgramClosedPluginEvent.class,
|
||||||
TraceOpenedPluginEvent.class, //
|
TraceOpenedPluginEvent.class,
|
||||||
TraceClosedPluginEvent.class, //
|
TraceClosedPluginEvent.class,
|
||||||
}, //
|
},
|
||||||
servicesRequired = { //
|
servicesRequired = {
|
||||||
DebuggerTraceManagerService.class, //
|
DebuggerTraceManagerService.class,
|
||||||
DebuggerModelService.class, //
|
DebuggerModelService.class,
|
||||||
DebuggerStaticMappingService.class, //
|
DebuggerStaticMappingService.class,
|
||||||
}, //
|
},
|
||||||
servicesProvided = { //
|
servicesProvided = {
|
||||||
DebuggerLogicalBreakpointService.class, //
|
DebuggerLogicalBreakpointService.class,
|
||||||
} //
|
})
|
||||||
)
|
|
||||||
public class DebuggerLogicalBreakpointServicePlugin extends Plugin
|
public class DebuggerLogicalBreakpointServicePlugin extends Plugin
|
||||||
implements DebuggerLogicalBreakpointService {
|
implements DebuggerLogicalBreakpointService {
|
||||||
|
|
||||||
|
@ -154,8 +153,9 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin
|
||||||
protected class TrackMappingsListener implements DebuggerStaticMappingChangeListener {
|
protected class TrackMappingsListener implements DebuggerStaticMappingChangeListener {
|
||||||
@Override
|
@Override
|
||||||
public void mappingsChanged(Set<Trace> affectedTraces, Set<Program> affectedPrograms) {
|
public void mappingsChanged(Set<Trace> affectedTraces, Set<Program> affectedPrograms) {
|
||||||
Msg.debug(this, "Mappings changed: " + affectedTraces + "," + affectedPrograms);
|
// Msg.debug(this, "Mappings changed: " + affectedTraces + "," + affectedPrograms);
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
|
// Msg.debug(this, "Processing map change");
|
||||||
Set<Trace> additionalTraces = new HashSet<>(affectedTraces);
|
Set<Trace> additionalTraces = new HashSet<>(affectedTraces);
|
||||||
Set<Program> additionalPrograms = new HashSet<>(affectedPrograms);
|
Set<Program> additionalPrograms = new HashSet<>(affectedPrograms);
|
||||||
try (RemoveCollector r = new RemoveCollector(changeListeners.fire)) {
|
try (RemoveCollector r = new RemoveCollector(changeListeners.fire)) {
|
||||||
|
@ -209,20 +209,21 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin
|
||||||
}
|
}
|
||||||
|
|
||||||
private void objectRestored() {
|
private void objectRestored() {
|
||||||
Msg.debug(this, "Restored: " + info.trace);
|
// Msg.debug(this, "Restored: " + info.trace);
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
info.reloadBreakpoints();
|
info.reloadBreakpoints();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void breakpointAdded(TraceBreakpoint breakpoint) {
|
private void breakpointAdded(TraceBreakpoint breakpoint) {
|
||||||
Msg.debug(this, "Breakpoint added: " + breakpoint);
|
// Msg.debug(this, "Breakpoint added: " + breakpoint);
|
||||||
if (!breakpoint.getLifespan().contains(info.recorder.getSnap())) {
|
if (!breakpoint.getLifespan().contains(info.recorder.getSnap())) {
|
||||||
// NOTE: User/script probably added historical breakpoint
|
// NOTE: User/script probably added historical breakpoint
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try (AddCollector c = new AddCollector(changeListeners.fire)) {
|
try (AddCollector c = new AddCollector(changeListeners.fire)) {
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
|
// Msg.debug(this, "Processing breakpoint add");
|
||||||
info.trackTraceBreakpoint(breakpoint, c, false);
|
info.trackTraceBreakpoint(breakpoint, c, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -241,7 +242,7 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin
|
||||||
|
|
||||||
private void breakpointLifespanChanged(TraceAddressSpace spaceIsNull,
|
private void breakpointLifespanChanged(TraceAddressSpace spaceIsNull,
|
||||||
TraceBreakpoint breakpoint, Range<Long> oldSpan, Range<Long> newSpan) {
|
TraceBreakpoint breakpoint, Range<Long> oldSpan, Range<Long> newSpan) {
|
||||||
Msg.debug(this, "Breakpoint span: " + breakpoint);
|
// Msg.debug(this, "Breakpoint span: " + breakpoint);
|
||||||
// NOTE: User/script probably modified historical breakpoint
|
// NOTE: User/script probably modified historical breakpoint
|
||||||
boolean isInOld = oldSpan.contains(info.recorder.getSnap());
|
boolean isInOld = oldSpan.contains(info.recorder.getSnap());
|
||||||
boolean isInNew = newSpan.contains(info.recorder.getSnap());
|
boolean isInNew = newSpan.contains(info.recorder.getSnap());
|
||||||
|
@ -265,7 +266,7 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin
|
||||||
}
|
}
|
||||||
|
|
||||||
private void breakpointDeleted(TraceBreakpoint breakpoint) {
|
private void breakpointDeleted(TraceBreakpoint breakpoint) {
|
||||||
Msg.debug(this, "Breakpoint deleted: " + breakpoint);
|
// Msg.debug(this, "Breakpoint deleted: " + breakpoint);
|
||||||
if (!breakpoint.getLifespan().contains(info.recorder.getSnap())) {
|
if (!breakpoint.getLifespan().contains(info.recorder.getSnap())) {
|
||||||
// NOTE: User/script probably removed historical breakpoint
|
// NOTE: User/script probably removed historical breakpoint
|
||||||
assert false;
|
assert false;
|
||||||
|
@ -295,7 +296,7 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin
|
||||||
}
|
}
|
||||||
|
|
||||||
private void objectRestored() {
|
private void objectRestored() {
|
||||||
Msg.debug(this, "Restored: " + info.program);
|
// Msg.debug(this, "Restored: " + info.program);
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
info.reloadBreakpoints();
|
info.reloadBreakpoints();
|
||||||
// NOTE: logical breakpoints should know which traces are affected
|
// NOTE: logical breakpoints should know which traces are affected
|
||||||
|
@ -315,8 +316,10 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin
|
||||||
}
|
}
|
||||||
|
|
||||||
private void breakpointBookmarkAdded(Bookmark bookmark) {
|
private void breakpointBookmarkAdded(Bookmark bookmark) {
|
||||||
|
// Msg.debug(this, "Breakpoint bookmark added: " + bookmark);
|
||||||
try (AddCollector c = new AddCollector(changeListeners.fire)) {
|
try (AddCollector c = new AddCollector(changeListeners.fire)) {
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
|
// Msg.debug(this, "Processing breakpoint bookmark add");
|
||||||
info.trackProgramBreakpoint(bookmark, c);
|
info.trackProgramBreakpoint(bookmark, c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -821,7 +824,7 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin
|
||||||
}
|
}
|
||||||
|
|
||||||
private void programOpened(Program program) {
|
private void programOpened(Program program) {
|
||||||
Msg.debug(this, "Opened Program: " + program);
|
// Msg.debug(this, "Opened Program: " + program);
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
if (program instanceof TraceProgramView) {
|
if (program instanceof TraceProgramView) {
|
||||||
// TODO: Not a good idea for user/script, but not prohibited
|
// TODO: Not a good idea for user/script, but not prohibited
|
||||||
|
|
|
@ -44,6 +44,11 @@ public class LoneLogicalBreakpoint implements LogicalBreakpointInternal {
|
||||||
this.justThisTrace = Set.of(recorder.getTrace());
|
this.justThisTrace = Set.of(recorder.getTrace());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format("<%s trace=%s>", getClass().getSimpleName(), breaks);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
return breaks.isEmpty();
|
return breaks.isEmpty();
|
||||||
|
|
|
@ -51,7 +51,7 @@ public class MappedLogicalBreakpoint implements LogicalBreakpointInternal {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return String.format("<MappedLogicalBreakpoint prog=%s, traces=%s>", progBreak,
|
return String.format("<%s prog=%s, traces=%s>", getClass().getSimpleName(), progBreak,
|
||||||
traceBreaks.values());
|
traceBreaks.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -191,7 +191,7 @@ public class DefaultBreakpointRecorder implements ManagedBreakpointRecorder {
|
||||||
Collection<TraceBreakpointKind> kinds) {
|
Collection<TraceBreakpointKind> kinds) {
|
||||||
for (TargetBreakpointLocation bl : bpts) {
|
for (TargetBreakpointLocation bl : bpts) {
|
||||||
String path = PathUtils.toString(bl.getPath());
|
String path = PathUtils.toString(bl.getPath());
|
||||||
recorder.parTx.execute("Breakpoint " + path + " toggled", () -> {
|
recorder.parTx.execute("Breakpoint " + path + " changed", () -> {
|
||||||
TraceBreakpoint traceBpt = recorder.getTraceBreakpoint(bl);
|
TraceBreakpoint traceBpt = recorder.getTraceBreakpoint(bl);
|
||||||
if (traceBpt == null) {
|
if (traceBpt == null) {
|
||||||
Msg.warn(this, "Cannot find toggled trace breakpoint for " + path);
|
Msg.warn(this, "Cannot find toggled trace breakpoint for " + path);
|
||||||
|
@ -210,7 +210,7 @@ public class DefaultBreakpointRecorder implements ManagedBreakpointRecorder {
|
||||||
spec.getLocations().thenAccept(bpts -> {
|
spec.getLocations().thenAccept(bpts -> {
|
||||||
doBreakpointSpecChanged(snap, bpts, enabled, kinds);
|
doBreakpointSpecChanged(snap, bpts, enabled, kinds);
|
||||||
}).exceptionally(ex -> {
|
}).exceptionally(ex -> {
|
||||||
Msg.error(this, "Error recording toggled breakpoint spec: " + spec.getJoinedPath("."),
|
Msg.error(this, "Error recording changed breakpoint spec: " + spec.getJoinedPath("."),
|
||||||
ex);
|
ex);
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
|
|
@ -502,7 +502,6 @@ public class DefaultTraceRecorder implements TraceRecorder {
|
||||||
return objectManager.getEventListener();
|
return objectManager.getEventListener();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public ListenerSet<TraceRecorderListener> getListeners() {
|
public ListenerSet<TraceRecorderListener> getListeners() {
|
||||||
return objectManager.getListeners();
|
return objectManager.getListeners();
|
||||||
}
|
}
|
||||||
|
@ -564,4 +563,8 @@ public class DefaultTraceRecorder implements TraceRecorder {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<Void> flushTransactions() {
|
||||||
|
return parTx.flush();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ package ghidra.app.plugin.core.debug.service.model;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
|
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
|
||||||
|
|
||||||
|
@ -74,4 +75,12 @@ public class PermanentTransactionExecutor {
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CompletableFuture<Void> flush() {
|
||||||
|
Runnable nop = () -> {
|
||||||
|
};
|
||||||
|
return CompletableFuture.allOf(Stream.of(threads)
|
||||||
|
.map(t -> CompletableFuture.runAsync(nop, t))
|
||||||
|
.toArray(CompletableFuture[]::new));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@ import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
import ghidra.app.services.TraceRecorder;
|
|
||||||
import ghidra.async.AsyncLazyMap;
|
import ghidra.async.AsyncLazyMap;
|
||||||
import ghidra.async.AsyncLazyMap.KeyedFuture;
|
import ghidra.async.AsyncLazyMap.KeyedFuture;
|
||||||
import ghidra.async.AsyncUtils;
|
import ghidra.async.AsyncUtils;
|
||||||
|
@ -32,7 +31,7 @@ import ghidra.util.TriConsumer;
|
||||||
|
|
||||||
public class RecorderComposedRegisterSet {
|
public class RecorderComposedRegisterSet {
|
||||||
|
|
||||||
private TraceRecorder recorder;
|
private DefaultTraceRecorder recorder;
|
||||||
|
|
||||||
protected final TriConsumer<Boolean, Boolean, Void> listenerRegAccChanged =
|
protected final TriConsumer<Boolean, Boolean, Void> listenerRegAccChanged =
|
||||||
this::registerAccessibilityChanged;
|
this::registerAccessibilityChanged;
|
||||||
|
@ -61,7 +60,7 @@ public class RecorderComposedRegisterSet {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public RecorderComposedRegisterSet(TraceRecorder recorder) {
|
public RecorderComposedRegisterSet(DefaultTraceRecorder recorder) {
|
||||||
this.recorder = recorder;
|
this.recorder = recorder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -457,7 +457,7 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
|
||||||
}
|
}
|
||||||
|
|
||||||
private void staticMappingAdded(TraceStaticMapping mapping) {
|
private void staticMappingAdded(TraceStaticMapping mapping) {
|
||||||
Msg.debug(this, "Trace Mapping added: " + mapping);
|
// Msg.debug(this, "Trace Mapping added: " + mapping);
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
MappingEntry me = new MappingEntry(mapping);
|
MappingEntry me = new MappingEntry(mapping);
|
||||||
putOutboundAndInboundEntries(me);
|
putOutboundAndInboundEntries(me);
|
||||||
|
|
|
@ -40,7 +40,6 @@ import ghidra.trace.model.stack.TraceStackFrame;
|
||||||
import ghidra.trace.model.thread.TraceThread;
|
import ghidra.trace.model.thread.TraceThread;
|
||||||
import ghidra.trace.model.time.TraceSnapshot;
|
import ghidra.trace.model.time.TraceSnapshot;
|
||||||
import ghidra.trace.model.time.TraceTimeManager;
|
import ghidra.trace.model.time.TraceTimeManager;
|
||||||
import ghidra.util.datastruct.ListenerSet;
|
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -480,5 +479,16 @@ public interface TraceRecorder {
|
||||||
@Internal
|
@Internal
|
||||||
TraceEventListener getListenerForRecord();
|
TraceEventListener getListenerForRecord();
|
||||||
|
|
||||||
ListenerSet<TraceRecorderListener> getListeners();
|
/**
|
||||||
|
* Wait for pending transactions finish execution.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The returned future will complete when queued transactions have been executed. There are no
|
||||||
|
* guarantees regarding transactions submitted after this future is returned. Furthermore, it
|
||||||
|
* may still be necessary to wait for the trace to finish invoking its domain object change
|
||||||
|
* listeners.
|
||||||
|
*
|
||||||
|
* @return the future which completes when pending transactions finish execution.
|
||||||
|
*/
|
||||||
|
CompletableFuture<Void> flushTransactions();
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,7 @@ import ghidra.app.services.*;
|
||||||
import ghidra.dbg.model.TestDebuggerModelBuilder;
|
import ghidra.dbg.model.TestDebuggerModelBuilder;
|
||||||
import ghidra.dbg.target.TargetBreakpointSpec.TargetBreakpointKind;
|
import ghidra.dbg.target.TargetBreakpointSpec.TargetBreakpointKind;
|
||||||
import ghidra.dbg.target.TargetBreakpointSpecContainer;
|
import ghidra.dbg.target.TargetBreakpointSpecContainer;
|
||||||
|
import ghidra.dbg.target.TargetTogglable;
|
||||||
import ghidra.dbg.testutil.DebuggerModelTestUtils;
|
import ghidra.dbg.testutil.DebuggerModelTestUtils;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
|
@ -44,6 +45,7 @@ import ghidra.test.ToyProgramBuilder;
|
||||||
import ghidra.trace.model.DefaultTraceLocation;
|
import ghidra.trace.model.DefaultTraceLocation;
|
||||||
import ghidra.trace.model.Trace;
|
import ghidra.trace.model.Trace;
|
||||||
import ghidra.trace.model.breakpoint.TraceBreakpoint;
|
import ghidra.trace.model.breakpoint.TraceBreakpoint;
|
||||||
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.database.UndoableTransaction;
|
import ghidra.util.database.UndoableTransaction;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
import help.screenshot.GhidraScreenShotGenerator;
|
import help.screenshot.GhidraScreenShotGenerator;
|
||||||
|
@ -86,6 +88,17 @@ public class DebuggerBreakpointsPluginScreenShots extends GhidraScreenShotGenera
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDownMine() {
|
public void tearDownMine() {
|
||||||
|
Msg.debug(this, "Tearing down");
|
||||||
|
Msg.debug(this, "Service breakpoints:");
|
||||||
|
for (LogicalBreakpoint lb : breakpointService.getAllBreakpoints()) {
|
||||||
|
Msg.debug(this, " bp: " + lb);
|
||||||
|
}
|
||||||
|
DebuggerBreakpointsProvider provider =
|
||||||
|
waitForComponentProvider(DebuggerBreakpointsProvider.class);
|
||||||
|
Msg.debug(this, "Provider breakpoints:");
|
||||||
|
for (LogicalBreakpointRow row : provider.breakpointTableModel.getModelData()) {
|
||||||
|
Msg.debug(this, " bp: " + row.getLogicalBreakpoint());
|
||||||
|
}
|
||||||
if (program != null) {
|
if (program != null) {
|
||||||
program.release(this);
|
program.release(this);
|
||||||
}
|
}
|
||||||
|
@ -112,9 +125,9 @@ public class DebuggerBreakpointsPluginScreenShots extends GhidraScreenShotGenera
|
||||||
traceManager.openTrace(trace1);
|
traceManager.openTrace(trace1);
|
||||||
traceManager.openTrace(trace3);
|
traceManager.openTrace(trace3);
|
||||||
|
|
||||||
mb.testProcess1.addRegion("echo:.text", mb.rng(0x00400000, 0x0040fff), "rx");
|
mb.testProcess1.addRegion("echo:.text", mb.rng(0x00400000, 0x00400fff), "rx");
|
||||||
mb.testProcess1.addRegion("echo:.data", mb.rng(0x00600000, 0x0060fff), "rw");
|
mb.testProcess1.addRegion("echo:.data", mb.rng(0x00600000, 0x00600fff), "rw");
|
||||||
mb.testProcess3.addRegion("echo:.text", mb.rng(0x7fac0000, 0x7facfff), "rx");
|
mb.testProcess3.addRegion("echo:.text", mb.rng(0x7fac0000, 0x7fac0fff), "rx");
|
||||||
|
|
||||||
try (UndoableTransaction tid = UndoableTransaction.start(trace1, "Add mapping", true)) {
|
try (UndoableTransaction tid = UndoableTransaction.start(trace1, "Add mapping", true)) {
|
||||||
mappingService.addMapping(
|
mappingService.addMapping(
|
||||||
|
@ -137,14 +150,13 @@ public class DebuggerBreakpointsPluginScreenShots extends GhidraScreenShotGenera
|
||||||
waitFor(() -> Unique.assertAtMostOne(recorder3.collectBreakpointContainers(null)),
|
waitFor(() -> Unique.assertAtMostOne(recorder3.collectBreakpointContainers(null)),
|
||||||
"No container");
|
"No container");
|
||||||
waitOn(bc3.placeBreakpoint(mb.addr(0x7fac1234), Set.of(TargetBreakpointKind.SW_EXECUTE)));
|
waitOn(bc3.placeBreakpoint(mb.addr(0x7fac1234), Set.of(TargetBreakpointKind.SW_EXECUTE)));
|
||||||
|
TargetTogglable bp3 = (TargetTogglable) waitForValue(
|
||||||
|
() -> Unique.assertAtMostOne(bc3.getCachedElements().values()));
|
||||||
|
waitOn(bp3.disable());
|
||||||
|
|
||||||
TraceBreakpoint bpt = waitForValue(() -> Unique.assertAtMostOne(
|
TraceBreakpoint bpt = waitForValue(() -> Unique.assertAtMostOne(
|
||||||
trace3.getBreakpointManager()
|
trace3.getBreakpointManager()
|
||||||
.getBreakpointsAt(recorder3.getSnap(), addr(trace3, 0x7fac1234))));
|
.getBreakpointsAt(recorder3.getSnap(), addr(trace3, 0x7fac1234))));
|
||||||
try (UndoableTransaction tid =
|
|
||||||
UndoableTransaction.start(trace3, "Disable breakpoint", true)) {
|
|
||||||
bpt.setEnabled(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
try (UndoableTransaction tid = UndoableTransaction.start(program, "Add breakpoint", true)) {
|
try (UndoableTransaction tid = UndoableTransaction.start(program, "Add breakpoint", true)) {
|
||||||
program.getBookmarkManager()
|
program.getBookmarkManager()
|
||||||
|
@ -156,9 +168,20 @@ public class DebuggerBreakpointsPluginScreenShots extends GhidraScreenShotGenera
|
||||||
}
|
}
|
||||||
|
|
||||||
waitForPass(() -> {
|
waitForPass(() -> {
|
||||||
assertEquals(3, breakpointService.getAllBreakpoints().size());
|
Set<LogicalBreakpoint> allBreakpoints = breakpointService.getAllBreakpoints();
|
||||||
|
assertEquals(3, allBreakpoints.size());
|
||||||
|
});
|
||||||
|
waitForPass(() -> {
|
||||||
assertFalse(bpt.isEnabled());
|
assertFalse(bpt.isEnabled());
|
||||||
});
|
});
|
||||||
|
/**
|
||||||
|
* TODO: Might be necessary to debounce and wait for service callbacks to settle. Sometimes,
|
||||||
|
* there are 3 for just a moment, and then additional callbacks mess things up.
|
||||||
|
*/
|
||||||
|
waitForPass(() -> {
|
||||||
|
assertEquals(3, provider.breakpointTable.getRowCount());
|
||||||
|
assertEquals(3, provider.locationTable.getRowCount());
|
||||||
|
});
|
||||||
|
|
||||||
captureIsolatedProvider(provider, 600, 600);
|
captureIsolatedProvider(provider, 600, 600);
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,6 +110,7 @@ public class DBTraceBreakpoint
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
enabled = (flagsByte & ENABLED_MASK) != 0;
|
enabled = (flagsByte & ENABLED_MASK) != 0;
|
||||||
|
// Msg.debug(this, "trace: breakpoint " + this + " enabled=" + enabled + ", because doFresh");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -154,6 +155,7 @@ public class DBTraceBreakpoint
|
||||||
this.comment = comment;
|
this.comment = comment;
|
||||||
update(PATH_COLUMN, NAME_COLUMN, THREADS_COLUMN, FLAGS_COLUMN, COMMENT_COLUMN);
|
update(PATH_COLUMN, NAME_COLUMN, THREADS_COLUMN, FLAGS_COLUMN, COMMENT_COLUMN);
|
||||||
this.enabled = enabled;
|
this.enabled = enabled;
|
||||||
|
// Msg.debug(this, "trace: breakpoint " + this + " enabled=" + enabled + ", because set");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void set(String path, String name, long[] threadKeys,
|
public void set(String path, String name, long[] threadKeys,
|
||||||
|
@ -342,11 +344,15 @@ public class DBTraceBreakpoint
|
||||||
this.kinds.clear();
|
this.kinds.clear();
|
||||||
this.kinds.addAll(kinds);
|
this.kinds.addAll(kinds);
|
||||||
this.enabled = enabled;
|
this.enabled = enabled;
|
||||||
|
// Msg.debug(this,
|
||||||
|
// "trace: breakpoint " + this + " enabled=" + enabled + ", because doSetFlags");
|
||||||
update(FLAGS_COLUMN);
|
update(FLAGS_COLUMN);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void doSetEnabled(boolean enabled) {
|
protected void doSetEnabled(boolean enabled) {
|
||||||
this.enabled = enabled;
|
this.enabled = enabled;
|
||||||
|
// Msg.debug(this,
|
||||||
|
// "trace: breakpoint " + this + " enabled=" + enabled + ", because doSetEnabled");
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
flagsByte |= ENABLED_MASK;
|
flagsByte |= ENABLED_MASK;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn;
|
import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A table model where the columns are enumerated, and the rows are wrappers on the objects being
|
* A table model where the columns are enumerated, and the rows are wrappers on the objects being
|
||||||
|
@ -44,11 +45,16 @@ public class RowWrappedEnumeratedColumnTableModel<C extends Enum<C> & Enumerated
|
||||||
this.wrapper = wrapper;
|
this.wrapper = wrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected synchronized R rowFor(T t) {
|
protected synchronized R addRowFor(T t) {
|
||||||
return map.computeIfAbsent(keyFunc.apply(t), k -> wrapper.apply(t));
|
R row = wrapper.apply(t);
|
||||||
|
R exists = map.put(keyFunc.apply(t), row);
|
||||||
|
if (exists != null) {
|
||||||
|
Msg.warn(this, "Replaced existing row! row=" + exists);
|
||||||
|
}
|
||||||
|
return row;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected synchronized R delFor(T t) {
|
protected synchronized R delRowFor(T t) {
|
||||||
return delKey(keyFunc.apply(t));
|
return delKey(keyFunc.apply(t));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,50 +62,75 @@ public class RowWrappedEnumeratedColumnTableModel<C extends Enum<C> & Enumerated
|
||||||
return map.remove(k);
|
return map.remove(k);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected synchronized List<R> rowsFor(Stream<? extends T> s) {
|
protected synchronized List<R> addRowsFor(Stream<? extends T> s) {
|
||||||
return s.map(this::rowFor).collect(Collectors.toList());
|
return s.map(this::addRowFor).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected synchronized List<R> rowsFor(Collection<? extends T> c) {
|
protected synchronized List<R> addRowsFor(Collection<? extends T> c) {
|
||||||
return rowsFor(c.stream());
|
return addRowsFor(c.stream());
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized R getRow(T t) {
|
public synchronized R getRow(T t) {
|
||||||
return map.get(keyFunc.apply(t));
|
return map.get(keyFunc.apply(t));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected synchronized List<R> getRows(Stream<? extends T> s) {
|
||||||
|
return s.map(this::getRow).filter(r -> r != null).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected synchronized List<R> getRows(Collection<? extends T> c) {
|
||||||
|
return getRows(c.stream());
|
||||||
|
}
|
||||||
|
|
||||||
public synchronized void addItem(T t) {
|
public synchronized void addItem(T t) {
|
||||||
if (map.containsKey(keyFunc.apply(t))) {
|
if (map.containsKey(keyFunc.apply(t))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
add(rowFor(t));
|
add(addRowFor(t));
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void addAllItems(Collection<? extends T> c) {
|
public synchronized void addAllItems(Collection<? extends T> c) {
|
||||||
Stream<? extends T> s = c.stream().filter(t -> !map.containsKey(keyFunc.apply(t)));
|
Stream<? extends T> s = c.stream().filter(t -> {
|
||||||
addAll(rowsFor(s));
|
K k = keyFunc.apply(t);
|
||||||
|
if (map.containsKey(k)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
addAll(addRowsFor(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateItem(T t) {
|
public void updateItem(T t) {
|
||||||
notifyUpdated(rowFor(t));
|
R row = getRow(t);
|
||||||
|
if (row == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
notifyUpdated(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateAllItems(Collection<T> c) {
|
public void updateAllItems(Collection<T> c) {
|
||||||
notifyUpdatedWith(rowsFor(c)::contains);
|
notifyUpdatedWith(getRows(c)::contains);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteItem(T t) {
|
public void deleteItem(T t) {
|
||||||
delete(delFor(t));
|
R row = delRowFor(t);
|
||||||
|
if (row == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
delete(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
public R deleteKey(K k) {
|
public R deleteKey(K k) {
|
||||||
R r = delKey(k);
|
R r = delKey(k);
|
||||||
|
if (r == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
delete(r);
|
delete(r);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void deleteAllItems(Collection<T> c) {
|
public synchronized void deleteAllItems(Collection<T> c) {
|
||||||
deleteWith(rowsFor(c)::contains);
|
deleteWith(getRows(c)::contains);
|
||||||
map.keySet().removeAll(c.stream().map(keyFunc).collect(Collectors.toList()));
|
map.keySet().removeAll(c.stream().map(keyFunc).collect(Collectors.toList()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue