diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpTcpDebuggerModelFactory.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpTcpDebuggerModelFactory.java
index e0dcd3957d..a57d52298d 100644
--- a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpTcpDebuggerModelFactory.java
+++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/client/GadpTcpDebuggerModelFactory.java
@@ -17,8 +17,7 @@ package ghidra.dbg.gadp.client;
import java.io.IOException;
import java.net.InetSocketAddress;
-import java.nio.channels.AsynchronousChannelGroup;
-import java.nio.channels.AsynchronousSocketChannel;
+import java.nio.channels.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
@@ -53,7 +52,7 @@ public class GadpTcpDebuggerModelFactory implements DebuggerModelFactory {
CompletableFuture connect = AsyncUtils.completable(TypeSpec.VOID,
channel::connect, new InetSocketAddress(host, port));
return connect.thenCompose(__ -> {
- GadpClient client = new GadpClient(host + ":" + port, channel);
+ GadpClient client = createClient(host + ":" + port, channel);
return client.connect().thenApply(___ -> client);
});
}
@@ -62,6 +61,10 @@ public class GadpTcpDebuggerModelFactory implements DebuggerModelFactory {
}
}
+ protected GadpClient createClient(String description, AsynchronousByteChannel channel) {
+ return new GadpClient(description, channel);
+ }
+
public String getAgentAddress() {
return host;
}
diff --git a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/server/AbstractGadpLocalDebuggerModelFactory.java b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/server/AbstractGadpLocalDebuggerModelFactory.java
index 15c373f836..a08e91aa51 100644
--- a/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/server/AbstractGadpLocalDebuggerModelFactory.java
+++ b/Ghidra/Debug/Debugger-gadp/src/main/java/ghidra/dbg/gadp/server/AbstractGadpLocalDebuggerModelFactory.java
@@ -18,6 +18,7 @@ package ghidra.dbg.gadp.server;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.ProcessBuilder.Redirect;
+import java.nio.channels.AsynchronousByteChannel;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@@ -84,10 +85,17 @@ public abstract class AbstractGadpLocalDebuggerModelFactory implements LocalDebu
this.jdwpPort = jdwpPort;
}
- @Override
- public CompletableFuture build() {
- CompletableFuture findPort = new CompletableFuture<>();
- new Thread(() -> {
+ class AgentThread extends Thread {
+ int port;
+ Process process;
+ CompletableFuture ready = new CompletableFuture<>();
+
+ public AgentThread() {
+ super(getThreadName());
+ }
+
+ @Override
+ public void run() {
try {
ProcessBuilder builder = new ProcessBuilder();
List cmd = new ArrayList<>();
@@ -101,9 +109,9 @@ public abstract class AbstractGadpLocalDebuggerModelFactory implements LocalDebu
builder.command(cmd);
builder.redirectError(Redirect.INHERIT);
- Process agent = builder.start();
+ process = builder.start();
BufferedReader reader =
- new BufferedReader(new InputStreamReader(agent.getInputStream()));
+ new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while (null != (line = reader.readLine())) {
if (LOG_AGENT_STDOUT) {
@@ -111,22 +119,61 @@ public abstract class AbstractGadpLocalDebuggerModelFactory implements LocalDebu
}
if (line.startsWith(AbstractGadpServer.LISTENING_ON)) {
String[] parts = line.split(":"); // Separates address from port
- findPort.complete(Integer.parseInt(parts[parts.length - 1]));
+ port = Integer.parseInt(parts[parts.length - 1]);
+ ready.complete(null);
}
}
- if (!findPort.isDone()) {
- findPort.completeExceptionally(
+ if (!ready.isDone()) {
+ ready.completeExceptionally(
new RuntimeException("Agent terminated unexpectedly"));
}
}
catch (Throwable e) {
- findPort.completeExceptionally(e);
+ ready.completeExceptionally(e);
}
- }, getThreadName()).start();
- return findPort.thenCompose(selectedPort -> {
- GadpTcpDebuggerModelFactory factory = new GadpTcpDebuggerModelFactory();
+ }
+ }
+
+ static class AgentOwningGadpClient extends GadpClient {
+ private final AgentThread agentThread;
+
+ public AgentOwningGadpClient(String description, AsynchronousByteChannel channel,
+ AgentThread agentThread) {
+ super(description, channel);
+ this.agentThread = agentThread;
+ }
+
+ @Override
+ public CompletableFuture close() {
+ return super.close().thenRun(() -> {
+ agentThread.process.destroy();
+ agentThread.interrupt();
+ });
+ }
+ }
+
+ static class AgentOwningGadpTcpDebuggerModelFactory extends GadpTcpDebuggerModelFactory {
+ private final AgentThread agentThread;
+
+ public AgentOwningGadpTcpDebuggerModelFactory(AgentThread agentThread) {
+ this.agentThread = agentThread;
+ }
+
+ @Override
+ protected GadpClient createClient(String description, AsynchronousByteChannel channel) {
+ return new AgentOwningGadpClient(description, channel, agentThread);
+ }
+ }
+
+ @Override
+ public CompletableFuture build() {
+ AgentThread thread = new AgentThread();
+ thread.start();
+ return thread.ready.thenCompose(__ -> {
+ GadpTcpDebuggerModelFactory factory =
+ new AgentOwningGadpTcpDebuggerModelFactory(thread);
// selectedPort may differ from port option, particularly if port == 0
- factory.setAgentPort(selectedPort);
+ factory.setAgentPort(thread.port);
return factory.build();
});
}
diff --git a/Ghidra/Debug/Debugger/certification.manifest b/Ghidra/Debug/Debugger/certification.manifest
index fb03a3e939..65c19f4f55 100644
--- a/Ghidra/Debug/Debugger/certification.manifest
+++ b/Ghidra/Debug/Debugger/certification.manifest
@@ -99,6 +99,7 @@ src/main/help/help/topics/DebuggerThreadsPlugin/images/stepback.png||GHIDRA||||E
src/main/help/help/topics/DebuggerThreadsPlugin/images/stepinto.png||GHIDRA||||END|
src/main/help/help/topics/DebuggerTimePlugin/DebuggerTimePlugin.html||GHIDRA||||END|
src/main/help/help/topics/DebuggerTimePlugin/images/DebuggerTimePlugin.png||GHIDRA||||END|
+src/main/help/help/topics/DebuggerTraceManagerServicePlugin/DebuggerTraceManagerServicePlugin.html||GHIDRA||||END|
src/main/resources/defaultTools/Debugger.tool||GHIDRA||||END|
src/main/resources/define_info_proc_mappings||GHIDRA||||END|
src/main/resources/images/add.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END|
diff --git a/Ghidra/Debug/Debugger/src/main/help/help/TOC_Source.xml b/Ghidra/Debug/Debugger/src/main/help/help/TOC_Source.xml
index 9cd0c3ccf4..833620d610 100644
--- a/Ghidra/Debug/Debugger/src/main/help/help/TOC_Source.xml
+++ b/Ghidra/Debug/Debugger/src/main/help/help/TOC_Source.xml
@@ -82,20 +82,24 @@
sortgroup="e"
target="help/topics/DebuggerThreadsPlugin/DebuggerThreadsPlugin.html" />
+
+
diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerListingPlugin/images/DebuggerListingPlugin.png b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerListingPlugin/images/DebuggerListingPlugin.png
index 4aee497608..b353abef39 100644
Binary files a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerListingPlugin/images/DebuggerListingPlugin.png and b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerListingPlugin/images/DebuggerListingPlugin.png differ
diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerThreadsPlugin/DebuggerThreadsPlugin.html b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerThreadsPlugin/DebuggerThreadsPlugin.html
index da02d1eac7..de4de4c2a5 100644
--- a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerThreadsPlugin/DebuggerThreadsPlugin.html
+++ b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerThreadsPlugin/DebuggerThreadsPlugin.html
@@ -130,17 +130,5 @@
translated, to the extent possible, into navigation coordinates and activated in Ghidra. For
example, if the user issues a frame command to the CLI of a GDB connection, then
Ghidra will navigate to that same frame.
-
- Save Trace
-
- This action is available whenever at least one trace is open and active. It saves the
- current trace. If the current trace is not in any project, it saves it under "New Traces" of
- the current project.
-
- Save Traces by Default
-
- This toggle is always available. If the tool is closed with this toggle enabled, all open
- traces are immediately saved. Note that if Ghidra is abruptly terminated (a rare occurrence
- under normal use), traces may not be saved.