mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-28 22:55:31 +08:00
GP-506: Added more actions to Debugger menu, and writing help.
This commit is contained in:
+6
-3
@@ -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<Void> 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;
|
||||
}
|
||||
|
||||
+61
-14
@@ -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<GadpClient> build() {
|
||||
CompletableFuture<Integer> findPort = new CompletableFuture<>();
|
||||
new Thread(() -> {
|
||||
class AgentThread extends Thread {
|
||||
int port;
|
||||
Process process;
|
||||
CompletableFuture<Void> ready = new CompletableFuture<>();
|
||||
|
||||
public AgentThread() {
|
||||
super(getThreadName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
ProcessBuilder builder = new ProcessBuilder();
|
||||
List<String> 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<Void> 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<GadpClient> 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();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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|
|
||||
|
||||
@@ -82,20 +82,24 @@
|
||||
sortgroup="e"
|
||||
target="help/topics/DebuggerThreadsPlugin/DebuggerThreadsPlugin.html" />
|
||||
|
||||
<tocdef id="DebuggerTraceManagerServicePlugin" text="Trace Management"
|
||||
sortgroup="f"
|
||||
target="help/topics/DebuggerTraceManagerServicePlugin/DebuggerTraceManagerServicePlugin.html" />
|
||||
|
||||
<tocdef id="DebuggerRegistersPlugin" text="Registers"
|
||||
sortgroup="f"
|
||||
sortgroup="g"
|
||||
target="help/topics/DebuggerRegistersPlugin/DebuggerRegistersPlugin.html" />
|
||||
|
||||
<tocdef id="DebuggerListingPlugin" text="Dynamic Listing"
|
||||
sortgroup="g"
|
||||
sortgroup="h"
|
||||
target="help/topics/DebuggerListingPlugin/DebuggerListingPlugin.html" />
|
||||
|
||||
<tocdef id="DebuggerStackPlugin" text="Stack"
|
||||
sortgroup="h"
|
||||
sortgroup="i"
|
||||
target="help/topics/DebuggerStackPlugin/DebuggerStackPlugin.html" />
|
||||
|
||||
<tocdef id="DebuggerBreakpointsPlugin" text="Breakpoints"
|
||||
sortgroup="i"
|
||||
sortgroup="j"
|
||||
target="help/topics/DebuggerBreakpointsPlugin/DebuggerBreakpointsPlugin.html" >
|
||||
|
||||
<tocdef id="DebuggerBreakpointMarkerPlugin" text="In the Listings"
|
||||
@@ -104,15 +108,15 @@
|
||||
</tocdef>
|
||||
|
||||
<tocdef id="DebuggerRegionsPlugin" text="Memory Regions"
|
||||
sortgroup="j"
|
||||
sortgroup="k"
|
||||
target="help/topics/DebuggerRegionsPlugin/DebuggerRegionsPlugin.html" />
|
||||
|
||||
<tocdef id="DebuggerTimePlugin" text="Time"
|
||||
sortgroup="k"
|
||||
sortgroup="l"
|
||||
target="help/topics/DebuggerTimePlugin/DebuggerTimePlugin.html" />
|
||||
|
||||
<tocdef id="DebuggerModulesPlugin" text="Modules and Sections"
|
||||
sortgroup="l"
|
||||
sortgroup="m"
|
||||
target="help/topics/DebuggerModulesPlugin/DebuggerModulesPlugin.html" >
|
||||
|
||||
<tocdef id="DebuggerStaticMappingPlugin" text="Static Mappings"
|
||||
@@ -121,7 +125,7 @@
|
||||
</tocdef>
|
||||
|
||||
<tocdef id="DebuggerBots" text="Bots: Workflow Automation"
|
||||
sortgroup="m"
|
||||
sortgroup="n"
|
||||
target="help/topics/DebuggerBots/DebuggerBots.html" />
|
||||
</tocdef>
|
||||
</tocref>
|
||||
|
||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
-12
@@ -130,17 +130,5 @@
|
||||
translated, to the extent possible, into navigation coordinates and activated in Ghidra. For
|
||||
example, if the user issues a <CODE>frame</CODE> command to the CLI of a GDB connection, then
|
||||
Ghidra will navigate to that same frame.</P>
|
||||
|
||||
<H3><A name="save_trace"></A>Save Trace</H3>
|
||||
|
||||
<P>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.</P>
|
||||
|
||||
<H3><A name="save_by_default"></A>Save Traces by Default</H3>
|
||||
|
||||
<P>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.</P>
|
||||
</BODY>
|
||||
</HTML>
|
||||
|
||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 21 KiB |
+75
@@ -0,0 +1,75 @@
|
||||
<!DOCTYPE doctype PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN">
|
||||
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<META name="generator" content=
|
||||
"HTML Tidy for Java (vers. 2009-12-01), see jtidy.sourceforge.net">
|
||||
|
||||
<TITLE>Debugger: Trace Service</TITLE>
|
||||
<META http-equiv="Content-Type" content="text/html; charset=windows-1252">
|
||||
<LINK rel="stylesheet" type="text/css" href="../../shared/Frontpage.css">
|
||||
</HEAD>
|
||||
|
||||
<BODY lang="EN-US">
|
||||
<H1><A name="plugin"></A>Debugger: Trace Management</H1>
|
||||
|
||||
<P>This service plugin manages the collection of open traces, and it controlled primarily via
|
||||
the <A href="help/topics/DebuggerThreadsPlugin/DebuggerThreadsPlugin.html">Threads</A> window.
|
||||
It maintains a list of open traces, the active trace coordinates (trace, time, thread, frame),
|
||||
and permits saving, opening, and closing traces. To some extent, it also tracks which traces
|
||||
are being actively recorded.</P>
|
||||
|
||||
<H2>Actions</H2>
|
||||
|
||||
<P>The plugin provides the following actions and toggles:</P>
|
||||
|
||||
<H3><A name="open_trace"></A>Open Trace</H3>
|
||||
|
||||
<P>This action is always available. It prompts for a trace in the current project and opens
|
||||
that trace in the tool.</P>
|
||||
|
||||
<H3><A name="save_trace"></A>Save Trace</H3>
|
||||
|
||||
<P>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.</P>
|
||||
|
||||
<H3><A name="close_trace"></A>Close Trace</H3>
|
||||
|
||||
<P>This action is available whenever at least one trace is open and active. It closes the
|
||||
current trace. <FONT color="red">WARNING:</FONT> If the trace has not been saved, it will be
|
||||
lost, even when Save by Default is active.</P>
|
||||
|
||||
<H3><A name="close_all_traces"></A>Close All Traces</H3>
|
||||
|
||||
<P>This action is available whenever at least one trace is open. It closes all traces in this
|
||||
tool. <FONT color="red">WARNING:</FONT> Any trace that has not been saved will be lost, even
|
||||
when Save by Default is active.</P>
|
||||
|
||||
<H3><A name="close_other_traces"></A>Close Other Traces</H3>
|
||||
|
||||
<P>This action is available whenever there is an open trace other than the active one --
|
||||
usually two or more open traces. It closes all traces in this tool, except the active trace.
|
||||
<FONT color="red">WARNING:</FONT> Any closed trace that has not been saved will be lost, even
|
||||
when Save by Default is active.</P>
|
||||
|
||||
<H3><A name="close_dead_traces"></A>Close Dead Traces</H3>
|
||||
|
||||
<P>This action is available whenever at least one trace is open. It closes all dead traces in
|
||||
this tool. <FONT color="red">WARNING:</FONT> Any closed trace that has not been saved will be
|
||||
lost, even when Save by Default is active.</P>
|
||||
|
||||
<H3><A name="save_by_default"></A>Save by Default</H3>
|
||||
|
||||
<P>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. When the tool is re-opened, the open traces are
|
||||
also restored.</P>
|
||||
|
||||
<H3><A name="auto_close_terminated"></A>Close Traces on Termination</H3>
|
||||
|
||||
<P>This toggle is always available. If a target terminates with this toggle enabled, and it was
|
||||
being recorded into a trace, that trace is automatically closed. If Save by Default is active,
|
||||
the trace is saved.</P>
|
||||
</BODY>
|
||||
</HTML>
|
||||
+54
-13
@@ -41,6 +41,7 @@ import ghidra.app.plugin.core.debug.gui.target.DebuggerTargetsPlugin;
|
||||
import ghidra.app.plugin.core.debug.gui.thread.DebuggerThreadsPlugin;
|
||||
import ghidra.app.plugin.core.debug.gui.time.DebuggerTimePlugin;
|
||||
import ghidra.app.plugin.core.debug.gui.watch.DebuggerWatchesPlugin;
|
||||
import ghidra.app.services.DebuggerTraceManagerService.BooleanChangeAdapter;
|
||||
import ghidra.app.services.MarkerService;
|
||||
import ghidra.framework.plugintool.Plugin;
|
||||
import ghidra.framework.plugintool.util.PluginUtils;
|
||||
@@ -258,6 +259,8 @@ public interface DebuggerResources {
|
||||
String GROUP_TARGET = "Dbg5. Target";
|
||||
String GROUP_BREAKPOINTS = "Dbg6. Breakpoints";
|
||||
String GROUP_TRACE = "Dbg7. Trace";
|
||||
String GROUP_TRACE_TOGGLES = "Dbg7.a. Trace Toggles";
|
||||
String GROUP_TRACE_CLOSE = "Dbg7.b. Trace Close";
|
||||
String GROUP_MAINTENANCE = "Dbg8. Maintenance";
|
||||
String GROUP_MAPPING = "Dbg9. Map Modules/Sections";
|
||||
|
||||
@@ -296,7 +299,7 @@ public interface DebuggerResources {
|
||||
}
|
||||
|
||||
interface SaveTraceAction {
|
||||
String NAME = "Save Trace";
|
||||
String NAME_PREFIX = "Save ";
|
||||
String DESCRIPTION = "Save the selected trace";
|
||||
Icon ICON = ICON_SAVE;
|
||||
String GROUP = GROUP_TRACE;
|
||||
@@ -304,9 +307,10 @@ public interface DebuggerResources {
|
||||
|
||||
static ActionBuilder builder(Plugin owner) {
|
||||
String ownerName = owner.getName();
|
||||
return new ActionBuilder(NAME, ownerName).description(DESCRIPTION)
|
||||
.toolBarIcon(ICON)
|
||||
.toolBarGroup(GROUP)
|
||||
return new ActionBuilder(NAME_PREFIX, ownerName).description(DESCRIPTION)
|
||||
.menuPath(DebuggerPluginPackage.NAME, NAME_PREFIX + "...")
|
||||
.menuIcon(ICON)
|
||||
.menuGroup(GROUP)
|
||||
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
|
||||
}
|
||||
}
|
||||
@@ -376,6 +380,9 @@ public interface DebuggerResources {
|
||||
return new ActionBuilder(NAME, owner.getName()).description(DESCRIPTION_PREFIX)
|
||||
.toolBarIcon(ICON)
|
||||
.toolBarGroup(GROUP)
|
||||
.menuPath(DebuggerPluginPackage.NAME, DESCRIPTION_PREFIX)
|
||||
.menuIcon(ICON)
|
||||
.menuGroup(GROUP)
|
||||
.helpLocation(new HelpLocation(helpOwner.getName(), HELP_ANCHOR));
|
||||
}
|
||||
}
|
||||
@@ -549,11 +556,10 @@ public interface DebuggerResources {
|
||||
Icon ICON = ICON_DISCONNECT;
|
||||
String HELP_ANCHOR = "disconnect_all";
|
||||
|
||||
public static ActionBuilder builder(Plugin owner) {
|
||||
String ownerName = owner.getName();
|
||||
return new ActionBuilder(ownerName, NAME).description(DESCRIPTION)
|
||||
public static ActionBuilder builder(Plugin owner, Plugin helpOwner) {
|
||||
return new ActionBuilder(owner.getName(), NAME).description(DESCRIPTION)
|
||||
.menuIcon(ICON)
|
||||
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
|
||||
.helpLocation(new HelpLocation(helpOwner.getName(), HELP_ANCHOR));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1166,9 +1172,9 @@ public interface DebuggerResources {
|
||||
}
|
||||
|
||||
interface SaveByDefaultAction {
|
||||
String NAME = "Save Trace By Default";
|
||||
String NAME = "Save Traces By Default";
|
||||
String DESCRIPTION = "Automatically save traces to the project";
|
||||
String GROUP = GROUP_TRACE;
|
||||
String GROUP = GROUP_TRACE_TOGGLES;
|
||||
Icon ICON = ICON_SAVE;
|
||||
String HELP_ANCHOR = "save_by_default";
|
||||
|
||||
@@ -1176,8 +1182,27 @@ public interface DebuggerResources {
|
||||
String ownerName = owner.getName();
|
||||
return new ToggleActionBuilder(NAME, ownerName)
|
||||
.description(DESCRIPTION)
|
||||
.toolBarGroup(GROUP)
|
||||
.toolBarIcon(ICON)
|
||||
.menuPath(DebuggerPluginPackage.NAME, NAME)
|
||||
.menuGroup(GROUP)
|
||||
.menuIcon(ICON)
|
||||
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
|
||||
}
|
||||
}
|
||||
|
||||
interface CloseOnTerminateAction {
|
||||
String NAME = "Close Traces Upon Termination";
|
||||
String DESCRIPTION = "Close any live trace whose recording terminates";
|
||||
String GROUP = GROUP_TRACE_TOGGLES;
|
||||
Icon ICON = ICON_CLOSE;
|
||||
String HELP_ANCHOR = "auto_close_terminated";
|
||||
|
||||
static ToggleActionBuilder builder(Plugin owner) {
|
||||
String ownerName = owner.getName();
|
||||
return new ToggleActionBuilder(NAME, ownerName)
|
||||
.description(DESCRIPTION)
|
||||
.menuPath(DebuggerPluginPackage.NAME, NAME)
|
||||
.menuGroup(GROUP)
|
||||
.menuIcon(ICON)
|
||||
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
|
||||
}
|
||||
}
|
||||
@@ -1204,7 +1229,7 @@ public interface DebuggerResources {
|
||||
interface CloseTraceAction {
|
||||
String NAME_PREFIX = "Close ";
|
||||
String DESCRIPTION = "Close the current trace";
|
||||
String GROUP = GROUP_TRACE;
|
||||
String GROUP = GROUP_TRACE_CLOSE;
|
||||
Icon ICON = ICON_CLOSE;
|
||||
String HELP_ANCHOR = "close_trace";
|
||||
|
||||
@@ -1329,4 +1354,20 @@ public interface DebuggerResources {
|
||||
}
|
||||
table.scrollToSelectedRow();
|
||||
}
|
||||
|
||||
public static class ToToggleSelectionListener implements BooleanChangeAdapter {
|
||||
private final ToggleDockingAction action;
|
||||
|
||||
public ToToggleSelectionListener(ToggleDockingAction action) {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changed(Boolean value) {
|
||||
if (action.isSelected() == value) {
|
||||
return;
|
||||
}
|
||||
action.setSelected(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -199,7 +199,7 @@ public class DebuggerTargetsProvider extends ComponentProviderAdapter {
|
||||
private void createActions() {
|
||||
actionConnect = new ConnectAction();
|
||||
actionDisconnect = new DisconnectAction();
|
||||
actionDisconnectAll = DisconnectAllAction.builder(plugin)
|
||||
actionDisconnectAll = DisconnectAllAction.builder(plugin, plugin)
|
||||
.menuPath(DisconnectAllAction.NAME)
|
||||
.onAction(this::activatedDisconnectAll)
|
||||
.buildAndInstallLocal(this);
|
||||
|
||||
+2
-48
@@ -137,22 +137,6 @@ public class DebuggerThreadsProvider extends ComponentProviderAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
protected static class ToToggleSelectionListener implements BooleanChangeAdapter {
|
||||
private final ToggleDockingAction action;
|
||||
|
||||
public ToToggleSelectionListener(ToggleDockingAction action) {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changed(Boolean value) {
|
||||
if (action.isSelected() == value) {
|
||||
return;
|
||||
}
|
||||
action.setSelected(value);
|
||||
}
|
||||
}
|
||||
|
||||
protected class SeekTracePresentAction extends AbstractSeekTracePresentAction
|
||||
implements BooleanChangeAdapter {
|
||||
public static final String GROUP = DebuggerResources.GROUP_GENERAL;
|
||||
@@ -289,7 +273,6 @@ public class DebuggerThreadsProvider extends ComponentProviderAdapter {
|
||||
StepTraceForwardAction actionStepTraceForward;
|
||||
SeekTracePresentAction actionSeekTracePresent;
|
||||
ToggleDockingAction actionSyncFocus;
|
||||
ToggleDockingAction actionSaveByDefault;
|
||||
Set<Object> strongRefs = new HashSet<>(); // Eww
|
||||
|
||||
public DebuggerThreadsProvider(final DebuggerThreadsPlugin plugin) {
|
||||
@@ -584,39 +567,17 @@ public class DebuggerThreadsProvider extends ComponentProviderAdapter {
|
||||
}
|
||||
|
||||
protected void createActions() {
|
||||
// TODO: Make other actions like this one
|
||||
actionSaveTrace = DebuggerResources.SaveTraceAction.builder(plugin)
|
||||
.enabledWhen(c -> current.getTrace() != null && traceManager != null)
|
||||
.onAction(c -> saveTrace())
|
||||
.buildAndInstallLocal(this);
|
||||
// TODO: Make other actions use builder?
|
||||
actionStepTraceBackward = new StepTraceBackwardAction();
|
||||
actionStepTraceForward = new StepTraceForwardAction();
|
||||
actionSeekTracePresent = new SeekTracePresentAction();
|
||||
actionSyncFocus = DebuggerResources.SynchronizeFocusAction.builder(plugin)
|
||||
actionSyncFocus = SynchronizeFocusAction.builder(plugin)
|
||||
.selected(traceManager != null && traceManager.isSynchronizeFocus())
|
||||
.enabledWhen(c -> traceManager != null)
|
||||
.onAction(c -> toggleSyncFocus(actionSyncFocus.isSelected()))
|
||||
.buildAndInstallLocal(this);
|
||||
traceManager.addSynchronizeFocusChangeListener(
|
||||
strongRef(new ToToggleSelectionListener(actionSyncFocus)));
|
||||
actionSaveByDefault = DebuggerResources.SaveByDefaultAction.builder(plugin)
|
||||
.selected(traceManager != null && traceManager.isSaveTracesByDefault())
|
||||
.enabledWhen(c -> traceManager != null)
|
||||
.onAction(c -> toggleSaveByDefault(actionSaveByDefault.isSelected()))
|
||||
.buildAndInstallLocal(this);
|
||||
traceManager.addSaveTracesByDefaultChangeListener(
|
||||
strongRef(new ToToggleSelectionListener(actionSaveByDefault)));
|
||||
}
|
||||
|
||||
private void saveTrace() {
|
||||
Trace curTrace = current.getTrace();
|
||||
if (curTrace == null) {
|
||||
return;
|
||||
}
|
||||
if (traceManager == null) {
|
||||
return;
|
||||
}
|
||||
traceManager.saveTrace(curTrace);
|
||||
}
|
||||
|
||||
private void toggleSyncFocus(boolean enabled) {
|
||||
@@ -626,13 +587,6 @@ public class DebuggerThreadsProvider extends ComponentProviderAdapter {
|
||||
traceManager.setSynchronizeFocus(enabled);
|
||||
}
|
||||
|
||||
private void toggleSaveByDefault(boolean enabled) {
|
||||
if (traceManager == null) {
|
||||
return;
|
||||
}
|
||||
traceManager.setSaveTracesByDefault(enabled);
|
||||
}
|
||||
|
||||
private void traceTabSelected(ListSelectionEvent e) {
|
||||
if (e.getValueIsAdjusting()) {
|
||||
return;
|
||||
|
||||
+1
-1
@@ -217,7 +217,7 @@ public class DebuggerModelServicePlugin extends Plugin
|
||||
}
|
||||
|
||||
protected void createActions() {
|
||||
actionDisconnectAll = DisconnectAllAction.builder(this)
|
||||
actionDisconnectAll = DisconnectAllAction.builder(this, this)
|
||||
.menuPath("Debugger", DisconnectAllAction.NAME)
|
||||
.onAction(this::activatedDisconnectAll)
|
||||
.buildAndInstall(tool);
|
||||
|
||||
+13
-2
@@ -26,8 +26,8 @@ import ghidra.app.events.ProgramActivatedPluginEvent;
|
||||
import ghidra.app.events.ProgramClosedPluginEvent;
|
||||
import ghidra.app.plugin.PluginCategoryNames;
|
||||
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
|
||||
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
|
||||
import ghidra.app.plugin.core.debug.gui.DebuggerResources.DebugProgramAction;
|
||||
import ghidra.app.plugin.core.debug.gui.DebuggerResources.DisconnectAllAction;
|
||||
import ghidra.app.plugin.core.debug.mapping.DebuggerTargetTraceMapper;
|
||||
import ghidra.app.plugin.core.debug.utils.BackgroundUtils;
|
||||
import ghidra.app.services.*;
|
||||
@@ -164,6 +164,7 @@ public class DebuggerModelServiceProxyPlugin extends Plugin
|
||||
new ProxiedRecorderChangeListener();
|
||||
|
||||
DockingAction actionDebugProgram;
|
||||
DockingAction actionDisconnectAll;
|
||||
|
||||
protected final ListenerSet<CollectionChangeListener<DebuggerModelFactory>> factoryListeners =
|
||||
new ListenerSet<>(CollectionChangeListener.of(DebuggerModelFactory.class));
|
||||
@@ -190,10 +191,15 @@ public class DebuggerModelServiceProxyPlugin extends Plugin
|
||||
|
||||
protected void createActions() {
|
||||
// Note, I have to give an enabledWhen, otherwise any context change re-enables it
|
||||
actionDebugProgram = DebuggerResources.DebugProgramAction.builder(this, delegate)
|
||||
actionDebugProgram = DebugProgramAction.builder(this, delegate)
|
||||
.enabledWhen(ctx -> currentProgramPath != null)
|
||||
.onAction(this::debugProgramActivated)
|
||||
.buildAndInstall(tool);
|
||||
actionDisconnectAll = DisconnectAllAction.builder(this, delegate)
|
||||
.menuPath("Debugger", DisconnectAllAction.NAME)
|
||||
.onAction(this::activatedDisconnectAll)
|
||||
.buildAndInstall(tool);
|
||||
|
||||
updateActionDebugProgram();
|
||||
}
|
||||
|
||||
@@ -209,6 +215,10 @@ public class DebuggerModelServiceProxyPlugin extends Plugin
|
||||
true, this::debugProgram);
|
||||
}
|
||||
|
||||
private void activatedDisconnectAll(ActionContext context) {
|
||||
closeAllModels();
|
||||
}
|
||||
|
||||
private CompletableFuture<Void> debugProgram(Program __, TaskMonitor monitor) {
|
||||
monitor.initialize(3);
|
||||
monitor.setMessage("Starting local session");
|
||||
@@ -244,6 +254,7 @@ public class DebuggerModelServiceProxyPlugin extends Plugin
|
||||
String desc = currentProgramPath == null ? DebugProgramAction.DESCRIPTION_PREFIX.trim()
|
||||
: DebugProgramAction.DESCRIPTION_PREFIX + currentProgramPath;
|
||||
actionDebugProgram.setDescription(desc);
|
||||
actionDebugProgram.getMenuBarData().setMenuItemName(desc);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+71
-3
@@ -23,6 +23,7 @@ import java.util.stream.Collectors;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.DockingAction;
|
||||
import docking.action.ToggleDockingAction;
|
||||
import ghidra.app.plugin.PluginCategoryNames;
|
||||
import ghidra.app.plugin.core.debug.DebuggerCoordinates;
|
||||
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
|
||||
@@ -125,12 +126,21 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
|
||||
// TODO: This is a bit out of this manager's bounds, but acceptable for now.
|
||||
class ForRecordersListener implements CollectionChangeListener<TraceRecorder> {
|
||||
@Override
|
||||
public void elementAdded(TraceRecorder element) {
|
||||
public void elementAdded(TraceRecorder recorder) {
|
||||
updateCurrentRecorder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void elementRemoved(TraceRecorder element) {
|
||||
public void elementRemoved(TraceRecorder recorder) {
|
||||
if (isAutoCloseOnTerminate()) {
|
||||
Trace trace = recorder.getTrace();
|
||||
if (getOpenTraces().contains(trace)) {
|
||||
if (isSaveTracesByDefault()) {
|
||||
saveTrace(trace);
|
||||
}
|
||||
closeTrace(trace);
|
||||
}
|
||||
}
|
||||
updateCurrentRecorder();
|
||||
}
|
||||
}
|
||||
@@ -149,6 +159,8 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
|
||||
protected final AsyncReference<Boolean, Void> saveTracesByDefault = new AsyncReference<>(true);
|
||||
@AutoConfigStateField(codec = BooleanAsyncConfigFieldCodec.class)
|
||||
protected final AsyncReference<Boolean, Void> synchronizeFocus = new AsyncReference<>(true);
|
||||
@AutoConfigStateField(codec = BooleanAsyncConfigFieldCodec.class)
|
||||
protected final AsyncReference<Boolean, Void> autoCloseOnTerminate = new AsyncReference<>(true);
|
||||
|
||||
// @AutoServiceConsumed via method
|
||||
private DebuggerModelService modelService;
|
||||
@@ -161,7 +173,11 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
|
||||
DockingAction actionCloseAllTraces;
|
||||
DockingAction actionCloseOtherTraces;
|
||||
DockingAction actionCloseDeadTraces;
|
||||
DockingAction actionSaveTrace;
|
||||
DockingAction actionOpenTrace;
|
||||
ToggleDockingAction actionSaveByDefault;
|
||||
ToggleDockingAction actionCloseOnTerminate;
|
||||
Set<Object> strongRefs = new HashSet<>(); // Eww
|
||||
|
||||
public DebuggerTraceManagerServicePlugin(PluginTool plugintool) {
|
||||
super(plugintool);
|
||||
@@ -169,6 +185,11 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
|
||||
autoServiceWiring = AutoService.wireServicesProvidedAndConsumed(this);
|
||||
}
|
||||
|
||||
private <T> T strongRef(T t) {
|
||||
strongRefs.add(t);
|
||||
return t;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
super.init();
|
||||
@@ -176,6 +197,10 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
|
||||
}
|
||||
|
||||
protected void createActions() {
|
||||
actionSaveTrace = SaveTraceAction.builder(this)
|
||||
.enabledWhen(c -> current.getTrace() != null)
|
||||
.onAction(this::activatedSaveTrace)
|
||||
.buildAndInstall(tool);
|
||||
actionOpenTrace = OpenTraceAction.builder(this)
|
||||
.enabledWhen(ctx -> true)
|
||||
.onAction(this::activatedOpenTrace)
|
||||
@@ -196,6 +221,28 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
|
||||
.enabledWhen(ctx -> !tracesView.isEmpty() && modelService != null)
|
||||
.onAction(this::activatedCloseDeadTraces)
|
||||
.buildAndInstall(tool);
|
||||
|
||||
actionSaveByDefault = SaveByDefaultAction.builder(this)
|
||||
.selected(isSaveTracesByDefault())
|
||||
.onAction(c -> setSaveTracesByDefault(actionSaveByDefault.isSelected()))
|
||||
.buildAndInstall(tool);
|
||||
addSaveTracesByDefaultChangeListener(
|
||||
strongRef(new ToToggleSelectionListener(actionSaveByDefault)));
|
||||
|
||||
actionCloseOnTerminate = CloseOnTerminateAction.builder(this)
|
||||
.selected(isAutoCloseOnTerminate())
|
||||
.onAction(c -> setAutoCloseOnTerminate(actionCloseOnTerminate.isSelected()))
|
||||
.buildAndInstall(tool);
|
||||
addAutoCloseOnTerminateChangeListener(
|
||||
strongRef(new ToToggleSelectionListener(actionCloseOnTerminate)));
|
||||
}
|
||||
|
||||
private void activatedSaveTrace(ActionContext ctx) {
|
||||
Trace trace = current.getTrace();
|
||||
if (trace == null) {
|
||||
return;
|
||||
}
|
||||
saveTrace(trace);
|
||||
}
|
||||
|
||||
private void activatedOpenTrace(ActionContext ctx) {
|
||||
@@ -445,6 +492,7 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
|
||||
Trace trace = current.getTrace();
|
||||
String name = trace == null ? "..." : trace.getName();
|
||||
actionCloseTrace.getMenuBarData().setMenuItemName(CloseTraceAction.NAME_PREFIX + name);
|
||||
actionSaveTrace.getMenuBarData().setMenuItemName(SaveTraceAction.NAME_PREFIX + name);
|
||||
tool.contextChanged(null);
|
||||
}
|
||||
|
||||
@@ -741,6 +789,7 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
|
||||
public void run(TaskMonitor monitor) throws CancelledException {
|
||||
try {
|
||||
traces.createFile(finalFilename, trace, monitor);
|
||||
trace.save("Initial save", monitor);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
// Done
|
||||
@@ -762,7 +811,6 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
|
||||
"Save New Trace Error", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -973,6 +1021,26 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
|
||||
saveTracesByDefault.removeChangeListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAutoCloseOnTerminate(boolean enabled) {
|
||||
autoCloseOnTerminate.set(enabled, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAutoCloseOnTerminate() {
|
||||
return autoCloseOnTerminate.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAutoCloseOnTerminateChangeListener(BooleanChangeAdapter listener) {
|
||||
autoCloseOnTerminate.addChangeListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAutoCloseOnTerminateChangeListener(BooleanChangeAdapter listener) {
|
||||
autoCloseOnTerminate.removeChangeListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
// TODO: Move this into some static util, now that canonical view is a trace concept
|
||||
public ProgramLocation fixLocation(ProgramLocation location, boolean matchSnap) {
|
||||
|
||||
+19
-1
@@ -162,7 +162,7 @@ public interface DebuggerTraceManagerService {
|
||||
/**
|
||||
* Check whether traces should by saved by default
|
||||
*
|
||||
* @return true if save by default, false otherwise
|
||||
* @return true if saved by default, false otherwise
|
||||
*/
|
||||
boolean isSaveTracesByDefault();
|
||||
|
||||
@@ -170,6 +170,24 @@ public interface DebuggerTraceManagerService {
|
||||
|
||||
void removeSaveTracesByDefaultChangeListener(BooleanChangeAdapter listener);
|
||||
|
||||
/**
|
||||
* Control whether live traces are automatically closed upon target termination
|
||||
*
|
||||
* @param enabled true to automatically close, false to leave open
|
||||
*/
|
||||
void setAutoCloseOnTerminate(boolean enabled);
|
||||
|
||||
/**
|
||||
* Check whether live traces are automatically closed upon target termination
|
||||
*
|
||||
* @return true if automatically closed, false if left open
|
||||
*/
|
||||
boolean isAutoCloseOnTerminate();
|
||||
|
||||
void addAutoCloseOnTerminateChangeListener(BooleanChangeAdapter listener);
|
||||
|
||||
void removeAutoCloseOnTerminateChangeListener(BooleanChangeAdapter listener);
|
||||
|
||||
/**
|
||||
* Swap out the trace view of a {@link ProgramLocation} if it is not the debugger's view
|
||||
*
|
||||
|
||||
+2
@@ -101,6 +101,8 @@ public class DebuggerThreadsPluginScreenShots extends GhidraScreenShotGenerator
|
||||
TraceRecorder recDummy2 =
|
||||
modelService.recordTarget(dummy2, new TestDebuggerTargetTraceMapper(dummy2));
|
||||
|
||||
traceManager.setAutoCloseOnTerminate(false);
|
||||
|
||||
traceManager.openTrace(recDummy1.getTrace());
|
||||
traceManager.openTrace(recDummy2.getTrace());
|
||||
recDummy1.stopRecording();
|
||||
|
||||
Reference in New Issue
Block a user