From 75ba9afb2dccdb718c842e42b2eabf798a27d983 Mon Sep 17 00:00:00 2001
From: Dan <46821332+nsadeveloper789@users.noreply.github.com>
Date: Tue, 30 Jan 2024 11:00:28 -0500
Subject: [PATCH] GP-3997: Move Debugger progress and errors to Debug Console
rather than pop-ups.
---
.../ghidra/app/services/ProgressService.java | 36 ++-
.../api/progress/CloseableTaskMonitor.java | 10 +
.../debug/api/progress/ProgressListener.java | 48 +++-
.../launcher/AbstractTraceRmiLaunchOffer.java | 13 +-
.../TraceRmiLauncherServicePlugin.java | 14 +-
.../service/tracermi/TraceRmiPlugin.java | 6 +
.../gui/console/DebuggerConsoleProvider.java | 7 +
.../gui/control/DebuggerControlPlugin.java | 15 +-
.../control/DebuggerMethodActionsPlugin.java | 2 +-
.../debug/gui/control/TargetActionTask.java | 27 ++-
.../DebuggerModelServiceProxyPlugin.java | 41 +++-
.../AbstractDebuggerProgramLaunchOffer.java | 226 +++++++++++-------
.../progress/DefaultCloseableTaskMonitor.java | 5 +
.../progress/DefaultMonitorReceiver.java | 5 +
.../progress/ProgressServicePlugin.java | 2 +-
15 files changed, 337 insertions(+), 120 deletions(-)
diff --git a/Ghidra/Debug/Debugger-api/src/main/java/ghidra/app/services/ProgressService.java b/Ghidra/Debug/Debugger-api/src/main/java/ghidra/app/services/ProgressService.java
index b99c776630..7a1b1ebafc 100644
--- a/Ghidra/Debug/Debugger-api/src/main/java/ghidra/app/services/ProgressService.java
+++ b/Ghidra/Debug/Debugger-api/src/main/java/ghidra/app/services/ProgressService.java
@@ -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.
+ *
+ *
+ * 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 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;
+ }
+ });
+ }
}
diff --git a/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/progress/CloseableTaskMonitor.java b/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/progress/CloseableTaskMonitor.java
index 58ced490e6..5697e12fbb 100644
--- a/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/progress/CloseableTaskMonitor.java
+++ b/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/progress/CloseableTaskMonitor.java
@@ -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);
}
diff --git a/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/progress/ProgressListener.java b/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/progress/ProgressListener.java
index 60749983f7..d8d506932b 100644
--- a/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/progress/ProgressListener.java
+++ b/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/progress/ProgressListener.java
@@ -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
+ *
+ *
+ * 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
+ *
+ *
+ * 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 {
*
show progress value in percent string
*
*
- * @param monitor the monitor
+ * @param monitor the receiver whose monitor's attribute(s) changed
*/
void attributeUpdated(MonitorReceiver monitor);
}
diff --git a/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/launcher/AbstractTraceRmiLaunchOffer.java b/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/launcher/AbstractTraceRmiLaunchOffer.java
index 063cf34de0..2f5ffc5a69 100644
--- a/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/launcher/AbstractTraceRmiLaunchOffer.java
+++ b/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/launcher/AbstractTraceRmiLaunchOffer.java
@@ -394,11 +394,11 @@ public abstract class AbstractTraceRmiLaunchOffer implements TraceRmiLaunchOffer
* Obtain the launcher args
*
*
- * 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);
diff --git a/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/launcher/TraceRmiLauncherServicePlugin.java b/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/launcher/TraceRmiLauncherServicePlugin.java
index 7756636506..4a9c0853c2 100644
--- a/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/launcher/TraceRmiLauncherServicePlugin.java
+++ b/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/launcher/TraceRmiLauncherServicePlugin.java
@@ -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() {
diff --git a/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/service/tracermi/TraceRmiPlugin.java b/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/service/tracermi/TraceRmiPlugin.java
index 7750e34924..e0734327f3 100644
--- a/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/service/tracermi/TraceRmiPlugin.java
+++ b/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/service/tracermi/TraceRmiPlugin.java
@@ -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
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/console/DebuggerConsoleProvider.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/console/DebuggerConsoleProvider.java
index c6634967f1..6ddddd172f 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/console/DebuggerConsoleProvider.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/console/DebuggerConsoleProvider.java
@@ -187,6 +187,8 @@ public class DebuggerConsoleProvider extends ComponentProviderAdapter
*
*
* This class is public for access by test cases only.
+ *
+ * @param the type of the message
*/
public interface LogRow {
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));
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/control/DebuggerControlPlugin.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/control/DebuggerControlPlugin.java
index 592599b71f..46d7308428 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/control/DebuggerControlPlugin.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/control/DebuggerControlPlugin.java
@@ -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 {
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/control/DebuggerMethodActionsPlugin.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/control/DebuggerMethodActionsPlugin.java
index 9ffcd44382..a02bcbc9ea 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/control/DebuggerMethodActionsPlugin.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/control/DebuggerMethodActionsPlugin.java
@@ -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));
}
}
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/control/TargetActionTask.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/control/TargetActionTask.java
index bcfcec2358..a3fd70154f 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/control/TargetActionTask.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/control/TargetActionTask.java
@@ -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 {
- entry.run(false);
+ 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);
+ }
}
}
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/DebuggerModelServiceProxyPlugin.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/DebuggerModelServiceProxyPlugin.java
index aa0622d7a4..35ff07b6ac 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/DebuggerModelServiceProxyPlugin.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/DebuggerModelServiceProxyPlugin.java
@@ -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,27 +374,41 @@ public class DebuggerModelServiceProxyPlugin extends Plugin
private void debugProgram(DebuggerProgramLaunchOffer offer, Program program,
PromptMode prompt) {
- BackgroundUtils.asyncModal(tool, offer.getButtonTitle(), true, true, m -> {
- List 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;
+
+ List recent = new ArrayList<>(readMostRecentLaunches(program));
+ recent.remove(offer.getConfigName());
+ recent.add(offer.getConfigName());
+ writeMostRecentLaunches(program, recent);
+ updateActionDebugProgram();
+
+ 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) {
+ return null;
+ }
+ return ExceptionUtils.rethrow(ex);
+ }).whenCompleteAsync((v, e) -> {
+ updateActionDebugProgram();
+ }, AsyncUtils.SWING_EXECUTOR);
});
- return offer.launchProgram(m, prompt).exceptionally(ex -> {
+ }
+ 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) {
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/launch/AbstractDebuggerProgramLaunchOffer.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/launch/AbstractDebuggerProgramLaunchOffer.java
index 5449811a0f..f1b1329a2c 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/launch/AbstractDebuggerProgramLaunchOffer.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/launch/AbstractDebuggerProgramLaunchOffer.java
@@ -105,6 +105,44 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg
return 10000;
}
+ protected static class TargetResult extends CompletableFuture
+ 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;
+ }
+ complete(object);
+ model.removeModelListener(this);
+ }
+
+ protected TargetObject onError(Throwable ex) {
+ model.removeModelListener(this);
+ return null;
+ }
+
+ @Override
+ public void created(TargetObject object) {
+ checkObject(object);
+ }
+
+ @Override
+ public void attributesChanged(TargetObject object, Collection removed,
+ Map added) {
+ if (!added.containsKey(TargetExecutionStateful.STATE_ATTRIBUTE_NAME)) {
+ return;
+ }
+ checkObject(object);
+ }
+ }
+
/**
* Listen for the launched target in the model
*
@@ -118,37 +156,33 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg
* @return a future that completes with the target object
*/
protected CompletableFuture listenForTarget(DebuggerObjectModel model) {
- var result = new CompletableFuture() {
- DebuggerModelListener listener = new DebuggerModelListener() {
- protected void checkObject(TargetObject object) {
- if (DebugModelConventions.liveProcessOrNull(object) == null) {
- return;
- }
- complete(object);
- model.removeModelListener(this);
- }
+ return new TargetResult(model);
+ }
- @Override
- public void created(TargetObject object) {
- checkObject(object);
- }
+ protected static class RecorderResult extends CompletableFuture
+ implements CollectionChangeListener {
+ private final DebuggerModelService service;
+ private final TargetObject target;
- @Override
- public void attributesChanged(TargetObject object, Collection removed,
- Map added) {
- if (!added.containsKey(TargetExecutionStateful.STATE_ATTRIBUTE_NAME)) {
- return;
- }
- checkObject(object);
- }
- };
- };
- model.addModelListener(result.listener);
- result.exceptionally(ex -> {
- model.removeModelListener(result.listener);
+ 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,74 +194,79 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg
*/
protected CompletableFuture listenForRecorder(DebuggerModelService service,
TargetObject target) {
- var result = new CompletableFuture() {
- CollectionChangeListener listener = new CollectionChangeListener<>() {
- @Override
- public void elementAdded(TraceRecorder element) {
- if (element.getTarget() == target) {
- complete(element);
- service.removeTraceRecordersChangedListener(this);
- }
- }
- };
- };
- service.addTraceRecordersChangedListener(result.listener);
- result.exceptionally(ex -> {
- service.removeTraceRecordersChangedListener(result.listener);
- return null;
- });
- return result;
+ return new RecorderResult(service, target);
}
- protected Address getMappingProbeAddress() {
- AddressIterator eepi = program.getSymbolTable().getExternalEntryPointIterator();
- if (eepi.hasNext()) {
- return eepi.next();
+ protected static class MappingResult extends CompletableFuture
+ 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();
}
- InstructionIterator ii = program.getListing().getInstructions(true);
- if (ii.hasNext()) {
- return ii.next().getAddress();
+
+ protected Void onError(Throwable ex) {
+ mappingService.removeChangeListener(this);
+ return null;
}
- AddressSetView es = program.getMemory().getExecuteSet();
- if (!es.isEmpty()) {
- return es.getMinAddress();
+
+ protected Address getMappingProbeAddress() {
+ AddressIterator eepi = program.getSymbolTable().getExternalEntryPointIterator();
+ if (eepi.hasNext()) {
+ return eepi.next();
+ }
+ InstructionIterator ii = program.getListing().getInstructions(true);
+ if (ii.hasNext()) {
+ return ii.next().getAddress();
+ }
+ AddressSetView es = program.getMemory().getExecuteSet();
+ if (!es.isEmpty()) {
+ return es.getMinAddress();
+ }
+ if (!program.getMemory().isEmpty()) {
+ return program.getMinAddress();
+ }
+ return null; // There's no hope
}
- if (!program.getMemory().isEmpty()) {
- return program.getMinAddress();
+
+ @Override
+ public void mappingsChanged(Set affectedTraces, Set affectedPrograms) {
+ if (!affectedPrograms.contains(program) &&
+ !affectedTraces.contains(trace)) {
+ return;
+ }
+ check();
+ }
+
+ protected void check() {
+ TraceLocation result =
+ mappingService.getOpenMappedLocation(trace, probe, recorder.getSnap());
+ if (result == null) {
+ return;
+ }
+ complete(null);
+ mappingService.removeChangeListener(this);
}
- return null; // There's no hope
}
protected CompletableFuture listenForMapping(
DebuggerStaticMappingService mappingService, TraceRecorder recorder) {
- ProgramLocation probe = new ProgramLocation(program, getMappingProbeAddress());
- Trace trace = recorder.getTrace();
- var result = new CompletableFuture() {
- DebuggerStaticMappingChangeListener listener = (affectedTraces, affectedPrograms) -> {
- if (!affectedPrograms.contains(program) &&
- !affectedTraces.contains(trace)) {
- return;
- }
- check();
- };
-
- protected void check() {
- TraceLocation result =
- mappingService.getOpenMappedLocation(trace, probe, recorder.getSnap());
- if (result == null) {
- return;
- }
- complete(null);
- mappingService.removeChangeListener(listener);
- }
- };
- mappingService.addChangeListener(result.listener);
- result.check();
- result.exceptionally(ex -> {
- mappingService.removeChangeListener(result.listener);
- return null;
- });
- return result;
+ return new MappingResult(mappingService, recorder, program);
}
protected Collection invokeMapper(TaskMonitor monitor,
@@ -396,15 +435,17 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg
* Obtain the launcher args
*
*
- * 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 getLauncherArgs(TargetLauncher launcher,
- boolean prompt, LaunchConfigurator configurator) {
+ public Map 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");
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/progress/DefaultCloseableTaskMonitor.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/progress/DefaultCloseableTaskMonitor.java
index 6366d0f76e..ddc1592ca9 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/progress/DefaultCloseableTaskMonitor.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/progress/DefaultCloseableTaskMonitor.java
@@ -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();
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/progress/DefaultMonitorReceiver.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/progress/DefaultMonitorReceiver.java
index 8b42f7ed86..f5d57182ef 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/progress/DefaultMonitorReceiver.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/progress/DefaultMonitorReceiver.java
@@ -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;
}
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/progress/ProgressServicePlugin.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/progress/ProgressServicePlugin.java
index 8a4e59988b..0a4001b04d 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/progress/ProgressServicePlugin.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/progress/ProgressServicePlugin.java
@@ -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 listeners = new ListenerSet<>(ProgressListener.class, true);