mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 10:19:23 +02:00
Merge remote-tracking branch 'origin/GP-3997_Dan_lessObtrusiveCommandFailures--SQUASHED'
This commit is contained in:
commit
68d209347c
15 changed files with 337 additions and 120 deletions
|
@ -16,12 +16,15 @@
|
|||
package ghidra.app.services;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
|
||||
import ghidra.debug.api.progress.*;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.framework.plugintool.ServiceInfo;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.Task;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
|
@ -99,4 +102,33 @@ public interface ProgressService {
|
|||
* @param listener the listener
|
||||
*/
|
||||
void removeProgressListener(ProgressListener listener);
|
||||
|
||||
/**
|
||||
* A drop-in replacement for {@link PluginTool#execute(Task)} that publishes progress via the
|
||||
* service rather than displaying a dialog.
|
||||
*
|
||||
* <p>
|
||||
* In addition to changing how progress is displayed, this also returns a future so that task
|
||||
* completion can be detected by the caller.
|
||||
*
|
||||
* @param task task to run in a new thread
|
||||
* @return a future which completes when the task is finished
|
||||
*/
|
||||
default CompletableFuture<Void> execute(Task task) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try (CloseableTaskMonitor monitor = publishTask()) {
|
||||
try {
|
||||
task.run(monitor);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
throw new CancellationException("User cancelled");
|
||||
}
|
||||
catch (Throwable e) {
|
||||
monitor.reportError(e);
|
||||
return ExceptionUtils.rethrow(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,17 @@ package ghidra.debug.api.progress;
|
|||
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* A task monitor that can be used in a try-with-resources block.
|
||||
*/
|
||||
public interface CloseableTaskMonitor extends TaskMonitor, AutoCloseable {
|
||||
@Override
|
||||
void close();
|
||||
|
||||
/**
|
||||
* Report an error while working on this task
|
||||
*
|
||||
* @param error the error
|
||||
*/
|
||||
void reportError(Throwable error);
|
||||
}
|
||||
|
|
|
@ -15,7 +15,13 @@
|
|||
*/
|
||||
package ghidra.debug.api.progress;
|
||||
|
||||
/**
|
||||
* A listener for events on the progress service, including updates to task progress
|
||||
*/
|
||||
public interface ProgressListener {
|
||||
/**
|
||||
* Describes how or why a task monitor was disposed
|
||||
*/
|
||||
enum Disposal {
|
||||
/**
|
||||
* The monitor was properly closed
|
||||
|
@ -27,12 +33,52 @@ public interface ProgressListener {
|
|||
CLEANED;
|
||||
}
|
||||
|
||||
/**
|
||||
* A new task monitor has been created
|
||||
*
|
||||
* <p>
|
||||
* The subscriber ought to display the monitor as soon as is reasonable. Optionally, a
|
||||
* subscriber may apply a grace period, e.g., half a second, before displaying it, in case it is
|
||||
* quickly disposed.
|
||||
*
|
||||
* @param monitor a means of retrieving messages and progress about the task
|
||||
*/
|
||||
void monitorCreated(MonitorReceiver monitor);
|
||||
|
||||
/**
|
||||
* A task monitor has been disposed
|
||||
*
|
||||
* @param monitor the receiver for the disposed monitor
|
||||
* @param disposal why it was disposed
|
||||
*/
|
||||
void monitorDisposed(MonitorReceiver monitor, Disposal disposal);
|
||||
|
||||
/**
|
||||
* A task has updated a monitor's message
|
||||
*
|
||||
* @param monitor the receiver whose monitor's message changed
|
||||
* @param message the new message
|
||||
*/
|
||||
void messageUpdated(MonitorReceiver monitor, String message);
|
||||
|
||||
/**
|
||||
* A task has reported an error
|
||||
*
|
||||
* @param monitor the receiver for the task reporting the error
|
||||
* @param error the exception representing the error
|
||||
*/
|
||||
void errorReported(MonitorReceiver monitor, Throwable error);
|
||||
|
||||
/**
|
||||
* A task's progress has updated
|
||||
*
|
||||
* <p>
|
||||
* Note the subscriber may need to use {@link MonitorReceiver#getMaximum()} to properly update
|
||||
* the display.
|
||||
*
|
||||
* @param monitor the receiver whose monitor's progress changed
|
||||
* @param progress the new progress value
|
||||
*/
|
||||
void progressUpdated(MonitorReceiver monitor, long progress);
|
||||
|
||||
/**
|
||||
|
@ -46,7 +92,7 @@ public interface ProgressListener {
|
|||
* <li>show progress value in percent string</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param monitor the monitor
|
||||
* @param monitor the receiver whose monitor's attribute(s) changed
|
||||
*/
|
||||
void attributeUpdated(MonitorReceiver monitor);
|
||||
}
|
||||
|
|
|
@ -394,11 +394,11 @@ public abstract class AbstractTraceRmiLaunchOffer implements TraceRmiLaunchOffer
|
|||
* Obtain the launcher args
|
||||
*
|
||||
* <p>
|
||||
* This should either call {@link #promptLauncherArgs(Map))} or
|
||||
* {@link #loadLastLauncherArgs(Map, boolean))}. Note if choosing the latter, the user will not
|
||||
* be prompted to confirm.
|
||||
* This should either call {@link #promptLauncherArgs(LaunchConfigurator, Throwable)} or
|
||||
* {@link #loadLastLauncherArgs(boolean)}. Note if choosing the latter, the user will not be
|
||||
* prompted to confirm.
|
||||
*
|
||||
* @param params the parameters of the model's launcher
|
||||
* @param prompt true to prompt the user, false to use saved arguments
|
||||
* @param configurator the rules for configuring the launcher
|
||||
* @param lastExc if retrying, the last exception to display as an error message
|
||||
* @return the chosen arguments, or null if the user cancels at the prompt
|
||||
|
@ -543,19 +543,24 @@ public abstract class AbstractTraceRmiLaunchOffer implements TraceRmiLaunchOffer
|
|||
|
||||
try {
|
||||
monitor.setMessage("Listening for connection");
|
||||
monitor.increment();
|
||||
acceptor = service.acceptOne(new InetSocketAddress("127.0.0.1", 0));
|
||||
monitor.setMessage("Launching back-end");
|
||||
monitor.increment();
|
||||
launchBackEnd(monitor, sessions, args, acceptor.getAddress());
|
||||
monitor.setMessage("Waiting for connection");
|
||||
monitor.increment();
|
||||
acceptor.setTimeout(getTimeoutMillis());
|
||||
connection = acceptor.accept();
|
||||
connection.registerTerminals(sessions.values());
|
||||
monitor.setMessage("Waiting for trace");
|
||||
monitor.increment();
|
||||
trace = connection.waitForTrace(getTimeoutMillis());
|
||||
traceManager.openTrace(trace);
|
||||
traceManager.activate(traceManager.resolveTrace(trace),
|
||||
ActivationCause.START_RECORDING);
|
||||
monitor.setMessage("Waiting for module mapping");
|
||||
monitor.increment();
|
||||
try {
|
||||
listenForMapping(mappingService, connection, trace).get(getTimeoutMillis(),
|
||||
TimeUnit.MILLISECONDS);
|
||||
|
|
|
@ -183,12 +183,22 @@ public class TraceRmiLauncherServicePlugin extends Plugin
|
|||
.toList();
|
||||
}
|
||||
|
||||
protected void executeTask(Task task) {
|
||||
ProgressService progressService = tool.getService(ProgressService.class);
|
||||
if (progressService != null) {
|
||||
progressService.execute(task);
|
||||
}
|
||||
else {
|
||||
tool.execute(task);
|
||||
}
|
||||
}
|
||||
|
||||
protected void relaunch(TraceRmiLaunchOffer offer) {
|
||||
tool.execute(new ReLaunchTask(offer));
|
||||
executeTask(new ReLaunchTask(offer));
|
||||
}
|
||||
|
||||
protected void configureAndLaunch(TraceRmiLaunchOffer offer) {
|
||||
tool.execute(new ConfigureAndLaunchTask(offer));
|
||||
executeTask(new ConfigureAndLaunchTask(offer));
|
||||
}
|
||||
|
||||
protected String[] constructLaunchMenuPrefix() {
|
||||
|
|
|
@ -31,6 +31,7 @@ import ghidra.framework.plugintool.*;
|
|||
import ghidra.framework.plugintool.AutoService.Wiring;
|
||||
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
|
||||
import ghidra.framework.plugintool.util.PluginStatus;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.Swing;
|
||||
import ghidra.util.datastruct.ListenerSet;
|
||||
import ghidra.util.task.ConsoleTaskMonitor;
|
||||
|
@ -63,6 +64,11 @@ public class TraceRmiPlugin extends Plugin implements InternalTraceRmiService {
|
|||
public void close() {
|
||||
// Nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportError(Throwable e) {
|
||||
Msg.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@AutoServiceConsumed
|
||||
|
|
|
@ -187,6 +187,8 @@ public class DebuggerConsoleProvider extends ComponentProviderAdapter
|
|||
*
|
||||
* <p>
|
||||
* This class is public for access by test cases only.
|
||||
*
|
||||
* @param <T> the type of the message
|
||||
*/
|
||||
public interface LogRow<T> {
|
||||
Icon getIcon();
|
||||
|
@ -319,6 +321,11 @@ public class DebuggerConsoleProvider extends ComponentProviderAdapter
|
|||
logTableModel.updateItem(logRow);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void errorReported(MonitorReceiver monitor, Throwable error) {
|
||||
log(DebuggerResources.ICON_LOG_ERROR, error.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void progressUpdated(MonitorReceiver monitor, long progress) {
|
||||
LogRow<?> logRow = logTableModel.getMap().get(contextFor(monitor));
|
||||
|
|
|
@ -146,6 +146,8 @@ public class DebuggerControlPlugin extends AbstractDebuggerPlugin
|
|||
private DebuggerControlService controlService;
|
||||
// @AutoServiceConsumed // via method
|
||||
private DebuggerEmulationService emulationService;
|
||||
@AutoServiceConsumed
|
||||
private ProgressService progressService;
|
||||
|
||||
public DebuggerControlPlugin(PluginTool tool) {
|
||||
super(tool);
|
||||
|
@ -282,8 +284,17 @@ public class DebuggerControlPlugin extends AbstractDebuggerPlugin
|
|||
updateActions();
|
||||
}
|
||||
|
||||
protected void executeTask(Task task) {
|
||||
if (progressService != null) {
|
||||
progressService.execute(task);
|
||||
}
|
||||
else {
|
||||
tool.execute(task);
|
||||
}
|
||||
}
|
||||
|
||||
protected void runTask(String title, ActionEntry entry) {
|
||||
tool.execute(new TargetActionTask(title, entry));
|
||||
executeTask(new TargetActionTask(tool, title, entry));
|
||||
}
|
||||
|
||||
protected void addTargetStepExtActions(Target target) {
|
||||
|
@ -359,7 +370,7 @@ public class DebuggerControlPlugin extends AbstractDebuggerPlugin
|
|||
if (target == null) {
|
||||
return;
|
||||
}
|
||||
tool.execute(new Task("Disconnect", false, false, false) {
|
||||
executeTask(new Task("Disconnect", false, false, false) {
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) throws CancelledException {
|
||||
try {
|
||||
|
|
|
@ -64,7 +64,7 @@ public class DebuggerMethodActionsPlugin extends Plugin implements PopupActionPr
|
|||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
tool.execute(new TargetActionTask(entry.display(), entry));
|
||||
tool.execute(new TargetActionTask(tool, entry.display(), entry));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,21 +15,42 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.debug.gui.control;
|
||||
|
||||
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
|
||||
import ghidra.app.services.DebuggerConsoleService;
|
||||
import ghidra.debug.api.target.Target.ActionEntry;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.Task;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
class TargetActionTask extends Task {
|
||||
private ActionEntry entry;
|
||||
private final PluginTool tool;
|
||||
private final ActionEntry entry;
|
||||
|
||||
public TargetActionTask(String title, ActionEntry entry) {
|
||||
public TargetActionTask(PluginTool tool, String title, ActionEntry entry) {
|
||||
super(title, false, false, false);
|
||||
this.tool = tool;
|
||||
this.entry = entry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) throws CancelledException {
|
||||
try {
|
||||
entry.run(false);
|
||||
}
|
||||
catch (Throwable e) {
|
||||
reportError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void reportError(Throwable error) {
|
||||
DebuggerConsoleService consoleService = tool.getService(DebuggerConsoleService.class);
|
||||
if (consoleService != null) {
|
||||
consoleService.log(DebuggerResources.ICON_LOG_ERROR, error.getMessage());
|
||||
}
|
||||
else {
|
||||
Msg.showError(this, null, "Control Error", error.getMessage(), error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@ import ghidra.dbg.target.TargetThread;
|
|||
import ghidra.debug.api.action.ActionSource;
|
||||
import ghidra.debug.api.model.*;
|
||||
import ghidra.debug.api.model.DebuggerProgramLaunchOffer.PromptMode;
|
||||
import ghidra.debug.api.progress.CloseableTaskMonitor;
|
||||
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
|
||||
import ghidra.framework.main.AppInfo;
|
||||
import ghidra.framework.main.FrontEndTool;
|
||||
|
@ -230,6 +231,8 @@ public class DebuggerModelServiceProxyPlugin extends Plugin
|
|||
private DebuggerTraceManagerService traceManager;
|
||||
@AutoServiceConsumed
|
||||
private DebuggerTargetService targetService;
|
||||
@AutoServiceConsumed
|
||||
private ProgressService progressService;
|
||||
@SuppressWarnings("unused")
|
||||
private final AutoService.Wiring autoServiceWiring;
|
||||
|
||||
|
@ -371,17 +374,15 @@ public class DebuggerModelServiceProxyPlugin extends Plugin
|
|||
|
||||
private void debugProgram(DebuggerProgramLaunchOffer offer, Program program,
|
||||
PromptMode prompt) {
|
||||
BackgroundUtils.asyncModal(tool, offer.getButtonTitle(), true, true, m -> {
|
||||
|
||||
List<String> recent = new ArrayList<>(readMostRecentLaunches(program));
|
||||
recent.remove(offer.getConfigName());
|
||||
recent.add(offer.getConfigName());
|
||||
writeMostRecentLaunches(program, recent);
|
||||
CompletableFuture.runAsync(() -> {
|
||||
updateActionDebugProgram();
|
||||
}, AsyncUtils.SWING_EXECUTOR).exceptionally(ex -> {
|
||||
Msg.error(this, "Trouble writing recent launches to program user data");
|
||||
return null;
|
||||
});
|
||||
|
||||
if (progressService == null) {
|
||||
BackgroundUtils.asyncModal(tool, offer.getButtonTitle(), true, true, m -> {
|
||||
return offer.launchProgram(m, prompt).exceptionally(ex -> {
|
||||
Throwable t = AsyncUtils.unwrapThrowable(ex);
|
||||
if (t instanceof CancellationException || t instanceof CancelledException) {
|
||||
|
@ -393,6 +394,22 @@ public class DebuggerModelServiceProxyPlugin extends Plugin
|
|||
}, AsyncUtils.SWING_EXECUTOR);
|
||||
});
|
||||
}
|
||||
else {
|
||||
@SuppressWarnings("resource")
|
||||
CloseableTaskMonitor monitor = progressService.publishTask();
|
||||
offer.launchProgram(monitor, prompt).exceptionally(ex -> {
|
||||
Throwable t = AsyncUtils.unwrapThrowable(ex);
|
||||
if (t instanceof CancellationException || t instanceof CancelledException) {
|
||||
return null;
|
||||
}
|
||||
monitor.reportError(t);
|
||||
return ExceptionUtils.rethrow(ex);
|
||||
}).whenCompleteAsync((v, e) -> {
|
||||
monitor.close();
|
||||
updateActionDebugProgram();
|
||||
}, AsyncUtils.SWING_EXECUTOR);
|
||||
}
|
||||
}
|
||||
|
||||
private void debugProgramButtonActivated(ActionContext ctx) {
|
||||
DebuggerProgramLaunchOffer offer = actionDebugProgram.getCurrentUserData();
|
||||
|
|
|
@ -105,21 +105,16 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg
|
|||
return 10000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen for the launched target in the model
|
||||
*
|
||||
* <p>
|
||||
* The abstract offer will invoke this before invoking the launch command, so there should be no
|
||||
* need to replay. Once the target has been found, the listener must remove itself. The default
|
||||
* is to just listen for the first live {@link TargetProcess} that appears. See
|
||||
* {@link DebugModelConventions#isProcessAlive(TargetProcess)}.
|
||||
*
|
||||
* @param model the model
|
||||
* @return a future that completes with the target object
|
||||
*/
|
||||
protected CompletableFuture<TargetObject> listenForTarget(DebuggerObjectModel model) {
|
||||
var result = new CompletableFuture<TargetObject>() {
|
||||
DebuggerModelListener listener = new DebuggerModelListener() {
|
||||
protected static class TargetResult extends CompletableFuture<TargetObject>
|
||||
implements DebuggerModelListener {
|
||||
private final DebuggerObjectModel model;
|
||||
|
||||
public TargetResult(DebuggerObjectModel model) {
|
||||
this.model = model;
|
||||
exceptionally(this::onError);
|
||||
model.addModelListener(this);
|
||||
}
|
||||
|
||||
protected void checkObject(TargetObject object) {
|
||||
if (DebugModelConventions.liveProcessOrNull(object) == null) {
|
||||
return;
|
||||
|
@ -128,6 +123,11 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg
|
|||
model.removeModelListener(this);
|
||||
}
|
||||
|
||||
protected TargetObject onError(Throwable ex) {
|
||||
model.removeModelListener(this);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void created(TargetObject object) {
|
||||
checkObject(object);
|
||||
|
@ -141,14 +141,48 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg
|
|||
}
|
||||
checkObject(object);
|
||||
}
|
||||
};
|
||||
};
|
||||
model.addModelListener(result.listener);
|
||||
result.exceptionally(ex -> {
|
||||
model.removeModelListener(result.listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen for the launched target in the model
|
||||
*
|
||||
* <p>
|
||||
* The abstract offer will invoke this before invoking the launch command, so there should be no
|
||||
* need to replay. Once the target has been found, the listener must remove itself. The default
|
||||
* is to just listen for the first live {@link TargetProcess} that appears. See
|
||||
* {@link DebugModelConventions#isProcessAlive(TargetProcess)}.
|
||||
*
|
||||
* @param model the model
|
||||
* @return a future that completes with the target object
|
||||
*/
|
||||
protected CompletableFuture<TargetObject> listenForTarget(DebuggerObjectModel model) {
|
||||
return new TargetResult(model);
|
||||
}
|
||||
|
||||
protected static class RecorderResult extends CompletableFuture<TraceRecorder>
|
||||
implements CollectionChangeListener<TraceRecorder> {
|
||||
private final DebuggerModelService service;
|
||||
private final TargetObject target;
|
||||
|
||||
public RecorderResult(DebuggerModelService service, TargetObject target) {
|
||||
this.service = service;
|
||||
this.target = target;
|
||||
exceptionally(this::onError);
|
||||
service.addTraceRecordersChangedListener(this);
|
||||
}
|
||||
|
||||
protected TraceRecorder onError(Throwable ex) {
|
||||
service.removeTraceRecordersChangedListener(this);
|
||||
return null;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void elementAdded(TraceRecorder element) {
|
||||
if (element.getTarget() == target) {
|
||||
complete(element);
|
||||
service.removeTraceRecordersChangedListener(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -160,23 +194,35 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg
|
|||
*/
|
||||
protected CompletableFuture<TraceRecorder> listenForRecorder(DebuggerModelService service,
|
||||
TargetObject target) {
|
||||
var result = new CompletableFuture<TraceRecorder>() {
|
||||
CollectionChangeListener<TraceRecorder> listener = new CollectionChangeListener<>() {
|
||||
@Override
|
||||
public void elementAdded(TraceRecorder element) {
|
||||
if (element.getTarget() == target) {
|
||||
complete(element);
|
||||
service.removeTraceRecordersChangedListener(this);
|
||||
return new RecorderResult(service, target);
|
||||
}
|
||||
|
||||
protected static class MappingResult extends CompletableFuture<Void>
|
||||
implements DebuggerStaticMappingChangeListener {
|
||||
private final DebuggerStaticMappingService mappingService;
|
||||
private final TraceRecorder recorder;
|
||||
private final Program program;
|
||||
|
||||
private final Trace trace;
|
||||
private final ProgramLocation probe;
|
||||
|
||||
public MappingResult(DebuggerStaticMappingService mappingService, TraceRecorder recorder,
|
||||
Program program) {
|
||||
this.mappingService = mappingService;
|
||||
this.recorder = recorder;
|
||||
this.program = program;
|
||||
|
||||
this.probe = new ProgramLocation(program, getMappingProbeAddress());
|
||||
this.trace = recorder.getTrace();
|
||||
|
||||
exceptionally(this::onError);
|
||||
mappingService.addChangeListener(this);
|
||||
check();
|
||||
}
|
||||
};
|
||||
};
|
||||
service.addTraceRecordersChangedListener(result.listener);
|
||||
result.exceptionally(ex -> {
|
||||
service.removeTraceRecordersChangedListener(result.listener);
|
||||
|
||||
protected Void onError(Throwable ex) {
|
||||
mappingService.removeChangeListener(this);
|
||||
return null;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
protected Address getMappingProbeAddress() {
|
||||
|
@ -198,18 +244,14 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg
|
|||
return null; // There's no hope
|
||||
}
|
||||
|
||||
protected CompletableFuture<Void> listenForMapping(
|
||||
DebuggerStaticMappingService mappingService, TraceRecorder recorder) {
|
||||
ProgramLocation probe = new ProgramLocation(program, getMappingProbeAddress());
|
||||
Trace trace = recorder.getTrace();
|
||||
var result = new CompletableFuture<Void>() {
|
||||
DebuggerStaticMappingChangeListener listener = (affectedTraces, affectedPrograms) -> {
|
||||
@Override
|
||||
public void mappingsChanged(Set<Trace> affectedTraces, Set<Program> affectedPrograms) {
|
||||
if (!affectedPrograms.contains(program) &&
|
||||
!affectedTraces.contains(trace)) {
|
||||
return;
|
||||
}
|
||||
check();
|
||||
};
|
||||
}
|
||||
|
||||
protected void check() {
|
||||
TraceLocation result =
|
||||
|
@ -218,16 +260,13 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg
|
|||
return;
|
||||
}
|
||||
complete(null);
|
||||
mappingService.removeChangeListener(listener);
|
||||
mappingService.removeChangeListener(this);
|
||||
}
|
||||
};
|
||||
mappingService.addChangeListener(result.listener);
|
||||
result.check();
|
||||
result.exceptionally(ex -> {
|
||||
mappingService.removeChangeListener(result.listener);
|
||||
return null;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
protected CompletableFuture<Void> listenForMapping(
|
||||
DebuggerStaticMappingService mappingService, TraceRecorder recorder) {
|
||||
return new MappingResult(mappingService, recorder, program);
|
||||
}
|
||||
|
||||
protected Collection<ModuleMapEntry> invokeMapper(TaskMonitor monitor,
|
||||
|
@ -396,15 +435,17 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg
|
|||
* Obtain the launcher args
|
||||
*
|
||||
* <p>
|
||||
* This should either call {@link #promptLauncherArgs(Map))} or
|
||||
* {@link #loadLastLauncherArgs(Map, boolean))}. Note if choosing the latter, the user will not
|
||||
* be prompted to confirm.
|
||||
* This should either call {@link #promptLauncherArgs(TargetLauncher,LaunchConfigurator)} or
|
||||
* {@link #loadLastLauncherArgs(TargetLauncher, boolean)}. Note if choosing the latter, the user
|
||||
* will not be prompted to confirm.
|
||||
*
|
||||
* @param params the parameters of the model's launcher
|
||||
* @param launcher the model's launcher
|
||||
* @param prompt true to prompt the user, false to use saved arguments
|
||||
* @param configurator a means of configuring the launcher
|
||||
* @return the chosen arguments, or null if the user cancels at the prompt
|
||||
*/
|
||||
public Map<String, ?> getLauncherArgs(TargetLauncher launcher,
|
||||
boolean prompt, LaunchConfigurator configurator) {
|
||||
public Map<String, ?> getLauncherArgs(TargetLauncher launcher, boolean prompt,
|
||||
LaunchConfigurator configurator) {
|
||||
return prompt
|
||||
? configurator.configureLauncher(launcher,
|
||||
promptLauncherArgs(launcher, configurator), RelPrompt.AFTER)
|
||||
|
@ -668,6 +709,7 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg
|
|||
}).thenApply(__ -> {
|
||||
if (locals.exception != null) {
|
||||
monitor.setMessage("Launch error: " + locals.exception);
|
||||
Msg.error(this, "Launch error", locals.exception);
|
||||
return locals.getResult();
|
||||
}
|
||||
monitor.setMessage("Launch successful");
|
||||
|
|
|
@ -69,6 +69,11 @@ public class DefaultCloseableTaskMonitor implements CloseableTaskMonitor {
|
|||
receiver.setMessage(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportError(Throwable error) {
|
||||
receiver.reportError(error);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return receiver.getMessage();
|
||||
|
|
|
@ -75,6 +75,10 @@ public class DefaultMonitorReceiver implements MonitorReceiver {
|
|||
plugin.listeners.invoke().messageUpdated(this, message);
|
||||
}
|
||||
|
||||
void reportError(Throwable error) {
|
||||
plugin.listeners.invoke().errorReported(this, error);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
synchronized (lock) {
|
||||
|
@ -158,6 +162,7 @@ public class DefaultMonitorReceiver implements MonitorReceiver {
|
|||
return cancelEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShowProgressValue() {
|
||||
return showProgressValue;
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ import ghidra.util.datastruct.ListenerSet;
|
|||
""",
|
||||
servicesProvided = { ProgressService.class },
|
||||
packageName = DebuggerPluginPackage.NAME,
|
||||
status = PluginStatus.STABLE)
|
||||
status = PluginStatus.RELEASED)
|
||||
public class ProgressServicePlugin extends Plugin implements ProgressService {
|
||||
ListenerSet<ProgressListener> listeners = new ListenerSet<>(ProgressListener.class, true);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue