mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 02:39:44 +02:00
GP-2189: Add FlatDebuggerAPI interface
This commit is contained in:
parent
58066601fc
commit
c7b464a0be
46 changed files with 4619 additions and 129 deletions
|
@ -261,6 +261,10 @@ public class DebuggerCoordinates {
|
|||
return all(trace, recorder, thread, view, newTime, frame);
|
||||
}
|
||||
|
||||
public DebuggerCoordinates withFrame(int newFrame) {
|
||||
return all(trace, recorder, thread, view, time, newFrame);
|
||||
}
|
||||
|
||||
public DebuggerCoordinates withView(TraceProgramView newView) {
|
||||
return all(trace, recorder, thread, newView, time, frame);
|
||||
}
|
||||
|
|
|
@ -196,7 +196,8 @@ public class DebuggerStaticSyncTrait {
|
|||
}
|
||||
|
||||
protected void doSyncCursorIntoStatic(ProgramLocation location) {
|
||||
if (location == null) {
|
||||
DebuggerStaticMappingService mappingService = this.mappingService;
|
||||
if (location == null || mappingService == null) {
|
||||
return;
|
||||
}
|
||||
ProgramLocation staticLoc = mappingService.getStaticLocationFromDynamic(location);
|
||||
|
@ -207,8 +208,10 @@ public class DebuggerStaticSyncTrait {
|
|||
}
|
||||
|
||||
protected void doSyncCursorFromStatic() {
|
||||
ProgramLocation currentStaticLocation = this.currentStaticLocation;
|
||||
TraceProgramView view = current.getView(); // NB. Used for snap (don't want emuSnap)
|
||||
if (view == null || currentStaticLocation == null) {
|
||||
DebuggerStaticMappingService mappingService = this.mappingService;
|
||||
if (currentStaticLocation == null || view == null || mappingService == null) {
|
||||
return;
|
||||
}
|
||||
ProgramLocation dynamicLoc =
|
||||
|
|
|
@ -18,6 +18,7 @@ package ghidra.app.plugin.core.debug.gui.breakpoint;
|
|||
import java.awt.Color;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
|
@ -861,7 +862,6 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin
|
|||
}
|
||||
|
||||
protected void doToggleBreakpointsAt(String title, ActionContext context) {
|
||||
// TODO: Seems like this should be in logical breakpoint service?
|
||||
if (breakpointService == null) {
|
||||
return;
|
||||
}
|
||||
|
@ -869,39 +869,21 @@ public class DebuggerBreakpointMarkerPlugin extends Plugin
|
|||
if (loc == null) {
|
||||
return;
|
||||
}
|
||||
Set<LogicalBreakpoint> bs = breakpointService.getBreakpointsAt(loc);
|
||||
if (bs == null || bs.isEmpty()) {
|
||||
breakpointService.toggleBreakpointsAt(loc, () -> {
|
||||
Set<TraceBreakpointKind> supported = getSupportedKindsFromContext(context);
|
||||
if (supported.isEmpty()) {
|
||||
breakpointError(title, "It seems this target does not support breakpoints.");
|
||||
return;
|
||||
return CompletableFuture.completedFuture(Set.of());
|
||||
}
|
||||
Set<TraceBreakpointKind> kinds = computeDefaultKinds(context, supported);
|
||||
long length = computeDefaultLength(context, kinds);
|
||||
placeBreakpointDialog.prompt(tool, breakpointService, title, loc, length, kinds, "");
|
||||
return;
|
||||
}
|
||||
State state = breakpointService.computeState(bs, loc);
|
||||
/**
|
||||
* If we're in the static listing, this will return null, indicating we should use the
|
||||
* program's perspective. The methods taking trace should accept a null trace and behave
|
||||
* accordingly. If in the dynamic listing, we act in the context of the returned trace.
|
||||
*/
|
||||
Trace trace = getTraceFromContext(context);
|
||||
boolean mapped = breakpointService.anyMapped(bs, trace);
|
||||
State toggled = state.getToggled(mapped);
|
||||
if (toggled.isEnabled()) {
|
||||
breakpointService.enableAll(bs, trace).exceptionally(ex -> {
|
||||
breakpointError(title, "Could not enable breakpoints", ex);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
else {
|
||||
breakpointService.disableAll(bs, trace).exceptionally(ex -> {
|
||||
breakpointError(title, "Could not disable breakpoints", ex);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
// Not great, but I'm not sticking around for the dialog
|
||||
return CompletableFuture.completedFuture(Set.of());
|
||||
}).exceptionally(ex -> {
|
||||
breakpointError(title, "Could not toggle breakpoints", ex);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1492,7 +1492,7 @@ public class DebuggerObjectsProvider extends ComponentProviderAdapter
|
|||
public void performLaunch(ActionContext context) {
|
||||
performAction(context, true, TargetLauncher.class, launcher -> {
|
||||
|
||||
Map<String, ?> args = launchOffer.getLauncherArgs(launcher.getParameters(), true);
|
||||
Map<String, ?> args = launchOffer.getLauncherArgs(launcher, true);
|
||||
if (args == null) {
|
||||
// Cancelled
|
||||
return AsyncUtils.NIL;
|
||||
|
|
|
@ -18,8 +18,7 @@ package ghidra.app.plugin.core.debug.service.breakpoint;
|
|||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.collections4.IteratorUtils;
|
||||
|
@ -785,7 +784,7 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin
|
|||
|
||||
protected void processChange(Consumer<ChangeCollector> processor, String description) {
|
||||
executor.submit(() -> {
|
||||
// Issue change callbacks without the lock! (try must surround sync)
|
||||
// Invoke change callbacks without the lock! (try must surround sync)
|
||||
try (ChangeCollector c = new ChangeCollector(changeListeners.fire)) {
|
||||
synchronized (lock) {
|
||||
processor.accept(c);
|
||||
|
@ -1206,6 +1205,29 @@ public class DebuggerLogicalBreakpointServicePlugin extends Plugin
|
|||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Set<LogicalBreakpoint>> toggleBreakpointsAt(ProgramLocation loc,
|
||||
Supplier<CompletableFuture<Set<LogicalBreakpoint>>> placer) {
|
||||
Set<LogicalBreakpoint> bs = getBreakpointsAt(loc);
|
||||
if (bs == null || bs.isEmpty()) {
|
||||
return placer.get();
|
||||
}
|
||||
State state = computeState(bs, loc);
|
||||
/**
|
||||
* If we're in the static listing, this will return null, indicating we should use the
|
||||
* program's perspective. The methods taking trace should accept a null trace and behave
|
||||
* accordingly. If in the dynamic listing, we act in the context of the returned trace.
|
||||
*/
|
||||
Trace trace =
|
||||
DebuggerLogicalBreakpointService.programOrTrace(loc, (p, a) -> null, (t, a) -> t);
|
||||
boolean mapped = anyMapped(bs, trace);
|
||||
State toggled = state.getToggled(mapped);
|
||||
if (toggled.isEnabled()) {
|
||||
return enableAll(bs, trace).thenApply(__ -> bs);
|
||||
}
|
||||
return disableAll(bs, trace).thenApply(__ -> bs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processEvent(PluginEvent event) {
|
||||
if (event instanceof ProgramOpenedPluginEvent) {
|
||||
|
|
|
@ -87,7 +87,8 @@ public class DebuggerModelServiceProxyPlugin extends Plugin
|
|||
private static final DebuggerProgramLaunchOffer DUMMY_LAUNCH_OFFER =
|
||||
new DebuggerProgramLaunchOffer() {
|
||||
@Override
|
||||
public CompletableFuture<Void> launchProgram(TaskMonitor monitor, boolean prompt) {
|
||||
public CompletableFuture<LaunchResult> launchProgram(TaskMonitor monitor,
|
||||
boolean prompt, LaunchConfigurator configurator) {
|
||||
throw new AssertionError("Who clicked me?");
|
||||
}
|
||||
|
||||
|
|
|
@ -562,9 +562,13 @@ public class DefaultTraceRecorder implements TraceRecorder {
|
|||
return true;
|
||||
}
|
||||
|
||||
// UNUSED?
|
||||
@Override
|
||||
public CompletableFuture<Void> flushTransactions() {
|
||||
return parTx.flush();
|
||||
return CompletableFuture.runAsync(() -> {
|
||||
}, privateQueue).thenCompose(__ -> {
|
||||
return objectManager.flushEvents();
|
||||
}).thenCompose(__ -> {
|
||||
return parTx.flush();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -285,4 +285,8 @@ public class TraceEventListener extends AnnotatedDebuggerAttributeListener {
|
|||
reorderer.dispose();
|
||||
}
|
||||
|
||||
public CompletableFuture<Void> flushEvents() {
|
||||
return reorderer.flushEvents();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -220,6 +220,10 @@ public class TraceObjectListener implements DebuggerModelListener {
|
|||
reorderer.dispose();
|
||||
}
|
||||
|
||||
public CompletableFuture<Void> flushEvents() {
|
||||
return reorderer.flushEvents();
|
||||
}
|
||||
|
||||
/*
|
||||
private CompletableFuture<List<TargetObject>> findDependenciesTop(TargetObject added) {
|
||||
List<TargetObject> result = new ArrayList<>();
|
||||
|
|
|
@ -702,4 +702,10 @@ public class TraceObjectManager {
|
|||
objectListener.dispose();
|
||||
}
|
||||
|
||||
public CompletableFuture<Void> flushEvents() {
|
||||
return eventListener.flushEvents().thenCompose(__ -> {
|
||||
return objectListener.flushEvents();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@ import java.util.stream.Collectors;
|
|||
|
||||
import javax.swing.JOptionPane;
|
||||
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.jdom.Element;
|
||||
import org.jdom.JDOMException;
|
||||
|
||||
|
@ -35,6 +34,7 @@ import ghidra.dbg.*;
|
|||
import ghidra.dbg.target.*;
|
||||
import ghidra.dbg.target.TargetLauncher.TargetCmdLineLauncher;
|
||||
import ghidra.dbg.target.TargetMethod.ParameterDescription;
|
||||
import ghidra.dbg.target.TargetMethod.TargetParameterMap;
|
||||
import ghidra.dbg.target.schema.TargetObjectSchema;
|
||||
import ghidra.dbg.util.PathUtils;
|
||||
import ghidra.framework.model.DomainFile;
|
||||
|
@ -276,11 +276,14 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg
|
|||
* @param params the parameters of the model's launcher
|
||||
* @return the arguments given by the user, or null if cancelled
|
||||
*/
|
||||
protected Map<String, ?> promptLauncherArgs(Map<String, ParameterDescription<?>> params) {
|
||||
protected Map<String, ?> promptLauncherArgs(TargetLauncher launcher,
|
||||
LaunchConfigurator configurator) {
|
||||
TargetParameterMap params = launcher.getParameters();
|
||||
DebuggerMethodInvocationDialog dialog =
|
||||
new DebuggerMethodInvocationDialog(tool, getButtonTitle(), "Launch", getIcon());
|
||||
// NB. Do not invoke read/writeConfigState
|
||||
Map<String, ?> args = loadLastLauncherArgs(params, true);
|
||||
Map<String, ?> args = configurator.configureLauncher(launcher,
|
||||
loadLastLauncherArgs(launcher, true), RelPrompt.BEFORE);
|
||||
for (ParameterDescription<?> param : params.values()) {
|
||||
Object val = args.get(param.name);
|
||||
if (val != null) {
|
||||
|
@ -311,13 +314,13 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg
|
|||
* @param forPrompt true if the user will be confirming the arguments
|
||||
* @return the loaded arguments, or defaults
|
||||
*/
|
||||
protected Map<String, ?> loadLastLauncherArgs(
|
||||
Map<String, ParameterDescription<?>> params, boolean forPrompt) {
|
||||
protected Map<String, ?> loadLastLauncherArgs(TargetLauncher launcher, boolean forPrompt) {
|
||||
/**
|
||||
* TODO: Supposedly, per-program, per-user config stuff is being generalized for analyzers.
|
||||
* Re-examine this if/when that gets merged
|
||||
*/
|
||||
if (program != null) {
|
||||
TargetParameterMap params = launcher.getParameters();
|
||||
ProgramUserData userData = program.getProgramUserData();
|
||||
String property =
|
||||
userData.getStringProperty(TargetCmdLineLauncher.CMDLINE_ARGS_NAME, null);
|
||||
|
@ -354,7 +357,6 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg
|
|||
}
|
||||
|
||||
return new LinkedHashMap<>();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -368,11 +370,17 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg
|
|||
* @param params the parameters of the model's launcher
|
||||
* @return the chosen arguments, or null if the user cancels at the prompt
|
||||
*/
|
||||
public Map<String, ?> getLauncherArgs(Map<String, ParameterDescription<?>> params,
|
||||
boolean prompt) {
|
||||
public Map<String, ?> getLauncherArgs(TargetLauncher launcher,
|
||||
boolean prompt, LaunchConfigurator configurator) {
|
||||
return prompt
|
||||
? promptLauncherArgs(params)
|
||||
: loadLastLauncherArgs(params, false);
|
||||
? configurator.configureLauncher(launcher,
|
||||
promptLauncherArgs(launcher, configurator), RelPrompt.AFTER)
|
||||
: configurator.configureLauncher(launcher, loadLastLauncherArgs(launcher, false),
|
||||
RelPrompt.NONE);
|
||||
}
|
||||
|
||||
public Map<String, ?> getLauncherArgs(TargetLauncher launcher, boolean prompt) {
|
||||
return getLauncherArgs(launcher, prompt, LaunchConfigurator.NOP);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -431,8 +439,9 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg
|
|||
}
|
||||
|
||||
protected CompletableFuture<DebuggerObjectModel> connect(DebuggerModelService service,
|
||||
boolean prompt) {
|
||||
boolean prompt, LaunchConfigurator configurator) {
|
||||
DebuggerModelFactory factory = getModelFactory();
|
||||
configurator.configureConnector(factory);
|
||||
if (prompt) {
|
||||
return service.showConnectDialog(factory);
|
||||
}
|
||||
|
@ -454,8 +463,8 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg
|
|||
|
||||
// Eww.
|
||||
protected CompletableFuture<Void> launch(TargetLauncher launcher,
|
||||
boolean prompt) {
|
||||
Map<String, ?> args = getLauncherArgs(launcher.getParameters(), prompt);
|
||||
boolean prompt, LaunchConfigurator configurator) {
|
||||
Map<String, ?> args = getLauncherArgs(launcher, prompt, configurator);
|
||||
if (args == null) {
|
||||
throw new CancellationException();
|
||||
}
|
||||
|
@ -551,17 +560,27 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg
|
|||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> launchProgram(TaskMonitor monitor, boolean prompt) {
|
||||
public CompletableFuture<LaunchResult> launchProgram(TaskMonitor monitor, boolean prompt,
|
||||
LaunchConfigurator configurator) {
|
||||
DebuggerModelService service = tool.getService(DebuggerModelService.class);
|
||||
DebuggerStaticMappingService mappingService =
|
||||
tool.getService(DebuggerStaticMappingService.class);
|
||||
monitor.initialize(6);
|
||||
monitor.setMessage("Connecting");
|
||||
var locals = new Object() {
|
||||
DebuggerObjectModel model;
|
||||
CompletableFuture<TargetObject> futureTarget;
|
||||
TargetObject target;
|
||||
TraceRecorder recorder;
|
||||
Throwable exception;
|
||||
|
||||
LaunchResult getResult() {
|
||||
return new LaunchResult(model, target, recorder, exception);
|
||||
}
|
||||
};
|
||||
return connect(service, prompt).thenCompose(m -> {
|
||||
return connect(service, prompt, configurator).thenCompose(m -> {
|
||||
checkCancelled(monitor);
|
||||
locals.model = m;
|
||||
monitor.incrementProgress(1);
|
||||
monitor.setMessage("Finding Launcher");
|
||||
return AsyncTimer.DEFAULT_TIMER.mark()
|
||||
|
@ -573,7 +592,7 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg
|
|||
monitor.setMessage("Launching");
|
||||
locals.futureTarget = listenForTarget(l.getModel());
|
||||
return AsyncTimer.DEFAULT_TIMER.mark()
|
||||
.timeOut(launch(l, prompt), getTimeoutMillis(),
|
||||
.timeOut(launch(l, prompt, configurator), getTimeoutMillis(),
|
||||
() -> onTimedOutLaunch(monitor));
|
||||
}).thenCompose(__ -> {
|
||||
checkCancelled(monitor);
|
||||
|
@ -584,6 +603,7 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg
|
|||
() -> onTimedOutTarget(monitor));
|
||||
}).thenCompose(t -> {
|
||||
checkCancelled(monitor);
|
||||
locals.target = t;
|
||||
monitor.incrementProgress(1);
|
||||
monitor.setMessage("Waiting for recorder");
|
||||
return AsyncTimer.DEFAULT_TIMER.mark()
|
||||
|
@ -591,6 +611,7 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg
|
|||
() -> onTimedOutRecorder(monitor, service, t));
|
||||
}).thenCompose(r -> {
|
||||
checkCancelled(monitor);
|
||||
locals.recorder = r;
|
||||
monitor.incrementProgress(1);
|
||||
if (r == null) {
|
||||
throw new CancellationException();
|
||||
|
@ -600,10 +621,16 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg
|
|||
.timeOut(listenForMapping(mappingService, r), getTimeoutMillis(),
|
||||
() -> onTimedOutMapping(monitor, mappingService, r));
|
||||
}).exceptionally(ex -> {
|
||||
if (AsyncUtils.unwrapThrowable(ex) instanceof CancellationException) {
|
||||
return null;
|
||||
locals.exception = AsyncUtils.unwrapThrowable(ex);
|
||||
return null;
|
||||
}).thenApply(__ -> {
|
||||
if (locals.exception != null) {
|
||||
monitor.setMessage("Launch error: " + locals.exception);
|
||||
return locals.getResult();
|
||||
}
|
||||
return ExceptionUtils.rethrow(ex);
|
||||
monitor.setMessage("Launch successful");
|
||||
monitor.incrementProgress(1);
|
||||
return locals.getResult();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,11 +15,17 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.debug.service.model.launch;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import javax.swing.Icon;
|
||||
|
||||
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
|
||||
import ghidra.app.services.TraceRecorder;
|
||||
import ghidra.dbg.DebuggerModelFactory;
|
||||
import ghidra.dbg.DebuggerObjectModel;
|
||||
import ghidra.dbg.target.TargetLauncher;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
|
@ -32,6 +38,89 @@ import ghidra.util.task.TaskMonitor;
|
|||
*/
|
||||
public interface DebuggerProgramLaunchOffer {
|
||||
|
||||
/**
|
||||
* The result of launching a program
|
||||
*
|
||||
* <p>
|
||||
* The launch may not always be completely successful. Instead of tearing things down, partial
|
||||
* launches are left in place, in case the user wishes to repair/complete the steps manually. If
|
||||
* the result includes a recorder, the launch was completed successfully. If not, then the
|
||||
* caller can choose how to treat the connection and target. If the cause of failure was an
|
||||
* exception, it is included. If the launch succeeded, but module mapping failed, the result
|
||||
* will include a recorder and the exception.
|
||||
*
|
||||
* @param model the connection
|
||||
* @param target the launched target
|
||||
* @param recorder the recorder
|
||||
* @param exception optional error, if failed
|
||||
*/
|
||||
public record LaunchResult(DebuggerObjectModel model, TargetObject target,
|
||||
TraceRecorder recorder, Throwable exception) {
|
||||
public static LaunchResult totalFailure(Throwable ex) {
|
||||
return new LaunchResult(null, null, null, ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When programmatically customizing launch configuration, describes callback timing relative to
|
||||
* prompting the user.
|
||||
*/
|
||||
public enum RelPrompt {
|
||||
/**
|
||||
* The user is not prompted for parameters. This will be the only callback.
|
||||
*/
|
||||
NONE,
|
||||
/**
|
||||
* The user will be prompted. This callback can pre-populate suggested parameters. Another
|
||||
* callback will be issued if the user does not cancel.
|
||||
*/
|
||||
BEFORE,
|
||||
/**
|
||||
* The user has confirmed the parameters. This callback can validate or override the users
|
||||
* parameters. Overriding the user is discouraged. This is the final callback.
|
||||
*/
|
||||
AFTER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callbacks for custom configuration when launching a program
|
||||
*/
|
||||
public interface LaunchConfigurator {
|
||||
LaunchConfigurator NOP = new LaunchConfigurator() {};
|
||||
|
||||
/**
|
||||
* Re-configure the factory, if desired
|
||||
*
|
||||
* @param factory the factory that will create the connection
|
||||
*/
|
||||
default void configureConnector(DebuggerModelFactory factory) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-write the launcher arguments, if desired
|
||||
*
|
||||
* @param launcher the launcher that will create the target
|
||||
* @param arguments the arguments suggested by the offer or saved settings
|
||||
* @param relPrompt describes the timing of this callback relative to prompting the user
|
||||
* @return the adjusted arguments
|
||||
*/
|
||||
default Map<String, ?> configureLauncher(TargetLauncher launcher,
|
||||
Map<String, ?> arguments, RelPrompt relPrompt) {
|
||||
return arguments;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch the program using the offered mechanism
|
||||
*
|
||||
* @param monitor a monitor for progress and cancellation
|
||||
* @param prompt if the user should be prompted to confirm launch parameters
|
||||
* @param configurator the configuration callbacks
|
||||
* @return a future which completes when the program is launched
|
||||
*/
|
||||
CompletableFuture<LaunchResult> launchProgram(TaskMonitor monitor, boolean prompt,
|
||||
LaunchConfigurator configurator);
|
||||
|
||||
/**
|
||||
* Launch the program using the offered mechanism
|
||||
*
|
||||
|
@ -39,7 +128,9 @@ public interface DebuggerProgramLaunchOffer {
|
|||
* @param prompt if the user should be prompted to confirm launch parameters
|
||||
* @return a future which completes when the program is launched
|
||||
*/
|
||||
CompletableFuture<Void> launchProgram(TaskMonitor monitor, boolean prompt);
|
||||
default CompletableFuture<LaunchResult> launchProgram(TaskMonitor monitor, boolean prompt) {
|
||||
return launchProgram(monitor, prompt, LaunchConfigurator.NOP);
|
||||
}
|
||||
|
||||
/**
|
||||
* A name so that this offer can be recognized later
|
||||
|
|
|
@ -599,7 +599,7 @@ public class DebuggerStaticMappingServicePlugin extends Plugin
|
|||
|
||||
@Override
|
||||
public CompletableFuture<Void> changesSettled() {
|
||||
return changeDebouncer.settled();
|
||||
return changeDebouncer.stable();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -18,6 +18,7 @@ package ghidra.app.services;
|
|||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import ghidra.app.plugin.core.debug.service.breakpoint.DebuggerLogicalBreakpointServicePlugin;
|
||||
import ghidra.app.services.LogicalBreakpoint.State;
|
||||
|
@ -351,4 +352,14 @@ public interface DebuggerLogicalBreakpointService {
|
|||
* @return a future which completes when the command has been processed
|
||||
*/
|
||||
CompletableFuture<Void> deleteLocs(Collection<TraceBreakpoint> col);
|
||||
|
||||
/**
|
||||
* Toggle the breakpoints at the given location
|
||||
*
|
||||
* @param location the location
|
||||
* @param placer if there are no breakpoints, a routine for placing a breakpoint
|
||||
* @return a future which completes when the command has been processed
|
||||
*/
|
||||
CompletableFuture<Set<LogicalBreakpoint>> toggleBreakpointsAt(ProgramLocation location,
|
||||
Supplier<CompletableFuture<Set<LogicalBreakpoint>>> placer);
|
||||
}
|
||||
|
|
|
@ -742,12 +742,13 @@ public interface LogicalBreakpoint {
|
|||
* Enable (or create) this breakpoint in the given target.
|
||||
*
|
||||
* <p>
|
||||
* Presuming the breakpoint is mappable to the given trace, if no breakpoint of the same kind
|
||||
* exists at the mapped address, then this will create a new breakpoint. Note, depending on the
|
||||
* debugging model, the enabled or created breakpoint may apply to more than the given trace.
|
||||
* If the breakpoint already exists, it is enabled. If it's already enabled, this has no effect.
|
||||
* If not, and the breakpoint is mappable to the given trace, the breakpoint is created. Note,
|
||||
* depending on the debugging model, the enabled or created breakpoint may affect other targets.
|
||||
* If the breakpoint is not mappable to the given trace, this has no effect.
|
||||
*
|
||||
* <p>
|
||||
* This simply issues the command. The logical breakpoint is updated only when the resulting
|
||||
* This simply issues the command(s). The logical breakpoint is updated only when the resulting
|
||||
* events are processed.
|
||||
*
|
||||
* @param trace the trace for the given target
|
||||
|
@ -761,7 +762,7 @@ public interface LogicalBreakpoint {
|
|||
* <p>
|
||||
* Note this will not create any new breakpoints. It will disable all breakpoints of the same
|
||||
* kind at the mapped address. Note, depending on the debugging model, the disabled breakpoint
|
||||
* may apply to more than the given trace.
|
||||
* may affect other targets.
|
||||
*
|
||||
* <p>
|
||||
* This simply issues the command. The logical breakpoint is updated only when the resulting
|
||||
|
@ -779,7 +780,7 @@ public interface LogicalBreakpoint {
|
|||
* This presumes the breakpoint's specifications are deletable. Note that if the logical
|
||||
* breakpoint is still mappable into this trace, a marker may be displayed, even though no
|
||||
* breakpoint is actually present. Note, depending on the debugging model, the deleted
|
||||
* breakpoint may be removed from more than the given trace.
|
||||
* breakpoint may be removed from other targets.
|
||||
*
|
||||
* This simply issues the command. The logical breakpoint is updated only when the resulting
|
||||
* events are processed.
|
||||
|
@ -794,7 +795,7 @@ public interface LogicalBreakpoint {
|
|||
*
|
||||
* <p>
|
||||
* This affects the mapped program, if applicable, and all open and live traces. Note, depending
|
||||
* on the debugging model, the enabled or created breakpoints may apply to more targets.
|
||||
* on the debugging model, the enabled or created breakpoints may affect other targets.
|
||||
*
|
||||
* <p>
|
||||
* This simply issues the command. The logical breakpoint is updated only when the resulting
|
||||
|
@ -809,7 +810,7 @@ public interface LogicalBreakpoint {
|
|||
*
|
||||
* <p>
|
||||
* This affects the mapped program, if applicable, and all open and live traces. Note, depending
|
||||
* on the debugging model, the disabled breakpoints may apply to more targets.
|
||||
* on the debugging model, the disabled breakpoints may affect other targets.
|
||||
*
|
||||
* <p>
|
||||
* This simply issues the command. The logical breakpoint is updated only when the resulting
|
||||
|
@ -825,7 +826,7 @@ public interface LogicalBreakpoint {
|
|||
* <p>
|
||||
* This presumes the breakpoint's specifications are deletable. This affects the mapped program,
|
||||
* if applicable, and all open and live traces. Note, depending on the debugging model, the
|
||||
* deleted breakpoints may be removed from more targets.
|
||||
* deleted breakpoints may be removed from other targets.
|
||||
*
|
||||
* <p>
|
||||
* This simply issues the command. The logical breakpoint is updated only when the resulting
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -25,7 +25,6 @@ import generic.Unique;
|
|||
import ghidra.app.plugin.core.debug.service.model.launch.DebuggerProgramLaunchOffer;
|
||||
import ghidra.app.plugin.core.debug.service.model.launch.DebuggerProgramLaunchOpinion;
|
||||
import ghidra.app.services.DebuggerModelService;
|
||||
import ghidra.async.AsyncUtils;
|
||||
import ghidra.dbg.DebuggerModelFactory;
|
||||
import ghidra.dbg.model.TestDebuggerModelFactory;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
|
@ -34,10 +33,12 @@ import ghidra.util.task.TaskMonitor;
|
|||
|
||||
public class TestDebuggerProgramLaunchOpinion implements DebuggerProgramLaunchOpinion {
|
||||
|
||||
static class TestDebuggerProgramLaunchOffer implements DebuggerProgramLaunchOffer {
|
||||
public static class TestDebuggerProgramLaunchOffer implements DebuggerProgramLaunchOffer {
|
||||
@Override
|
||||
public CompletableFuture<Void> launchProgram(TaskMonitor monitor, boolean prompt) {
|
||||
return AsyncUtils.NIL;
|
||||
public CompletableFuture<LaunchResult> launchProgram(TaskMonitor monitor, boolean prompt,
|
||||
LaunchConfigurator configurator) {
|
||||
return CompletableFuture
|
||||
.completedFuture(LaunchResult.totalFailure(new AssertionError()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.debug.service.model.record;
|
||||
|
||||
import static org.hamcrest.Matchers.isOneOf;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
@ -304,7 +304,7 @@ public class ObjectBasedTraceRecorderTest extends AbstractGhidraHeadedDebuggerGU
|
|||
mb.testProcess1.memory.setMemory(tb.addr(0x00400123), mb.arr(1, 2, 3, 4, 5, 6, 7, 8, 9));
|
||||
flushAndWait();
|
||||
assertThat(memory.getState(recorder.getSnap(), tb.addr(0x00400123)),
|
||||
isOneOf(null, TraceMemoryState.UNKNOWN));
|
||||
is(oneOf(null, TraceMemoryState.UNKNOWN)));
|
||||
|
||||
byte[] data = new byte[10];
|
||||
waitOn(recorder.readMemory(tb.addr(0x00400123), 10));
|
||||
|
@ -331,7 +331,7 @@ public class ObjectBasedTraceRecorderTest extends AbstractGhidraHeadedDebuggerGU
|
|||
mb.testProcess1.memory.setMemory(tb.addr(0x00400123), mb.arr(1, 2, 3, 4, 5, 6, 7, 8, 9));
|
||||
flushAndWait();
|
||||
assertThat(memory.getState(recorder.getSnap(), tb.addr(0x00400123)),
|
||||
isOneOf(null, TraceMemoryState.UNKNOWN));
|
||||
is(oneOf(null, TraceMemoryState.UNKNOWN)));
|
||||
|
||||
byte[] data = new byte[10];
|
||||
assertNull(waitOn(recorder.readMemoryBlocks(
|
||||
|
@ -354,7 +354,7 @@ public class ObjectBasedTraceRecorderTest extends AbstractGhidraHeadedDebuggerGU
|
|||
mb.testProcess1.memory.addRegion("exe:.text", mb.rng(0x00400000, 0x00400fff), "rwx");
|
||||
flushAndWait();
|
||||
assertThat(memory.getState(recorder.getSnap(), tb.addr(0x00400123)),
|
||||
isOneOf(null, TraceMemoryState.UNKNOWN));
|
||||
is(oneOf(null, TraceMemoryState.UNKNOWN)));
|
||||
|
||||
byte[] data = new byte[10];
|
||||
waitOn(recorder.writeMemory(tb.addr(0x00400123), tb.arr(1, 2, 3, 4, 5, 6, 7, 8, 9)));
|
||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue