mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-06 03:50:02 +02:00
GP-2968: per review comment (cleanup)
GP-2968: prompting ReviewTool GP-2968: my bad, NPE check was hanging the launch GP-2968: fix for passing Enums; fix for toMap'ing nulls GP-2968: refactoring args logic into loop w/ handling for exception GP-2968: fix for NPE & stored parameter errors
This commit is contained in:
parent
f930701ed6
commit
c43351d742
13 changed files with 159 additions and 92 deletions
|
@ -1382,9 +1382,8 @@ public class DbgManagerImpl implements DbgManager {
|
||||||
BitmaskSet<DebugVerifierFlags> vf =
|
BitmaskSet<DebugVerifierFlags> vf =
|
||||||
new BitmaskSet<DebugVerifierFlags>(DebugVerifierFlags.class,
|
new BitmaskSet<DebugVerifierFlags>(DebugVerifierFlags.class,
|
||||||
vfVal == null ? 0 : vfVal);
|
vfVal == null ? 0 : vfVal);
|
||||||
execute(new DbgLaunchProcessCommand(this, args,
|
return execute(new DbgLaunchProcessCommand(this, args,
|
||||||
initDir, env, cf, ef, vf));
|
initDir, env, cf, ef, vf)).thenApply(__ -> null);
|
||||||
return AsyncUtils.NIL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompletableFuture<?> openFile(Map<String, ?> args) {
|
public CompletableFuture<?> openFile(Map<String, ?> args) {
|
||||||
|
|
|
@ -82,7 +82,7 @@ public class DbgModelTargetContinuationOptionImpl extends DbgModelTargetObjectIm
|
||||||
public void setAttributes() {
|
public void setAttributes() {
|
||||||
changeAttributes(List.of(), List.of(), Map.of( //
|
changeAttributes(List.of(), List.of(), Map.of( //
|
||||||
DISPLAY_ATTRIBUTE_NAME, getName() + " : " + optionCont.description, //
|
DISPLAY_ATTRIBUTE_NAME, getName() + " : " + optionCont.description, //
|
||||||
VALUE_ATTRIBUTE_NAME, optionCont, //
|
VALUE_ATTRIBUTE_NAME, optionCont.val, //
|
||||||
ENABLED_ATTRIBUTE_NAME,
|
ENABLED_ATTRIBUTE_NAME,
|
||||||
optionCont.equals(DebugFilterContinuationOption.DEBUG_FILTER_GO_HANDLED)),
|
optionCont.equals(DebugFilterContinuationOption.DEBUG_FILTER_GO_HANDLED)),
|
||||||
"Refreshed");
|
"Refreshed");
|
||||||
|
|
|
@ -83,7 +83,7 @@ public class DbgModelTargetExecutionOptionImpl extends DbgModelTargetObjectImpl
|
||||||
public void setAttributes() {
|
public void setAttributes() {
|
||||||
changeAttributes(List.of(), List.of(), Map.of( //
|
changeAttributes(List.of(), List.of(), Map.of( //
|
||||||
DISPLAY_ATTRIBUTE_NAME, getName() + " : " + optionExc.description, //
|
DISPLAY_ATTRIBUTE_NAME, getName() + " : " + optionExc.description, //
|
||||||
VALUE_ATTRIBUTE_NAME, optionExc, //
|
VALUE_ATTRIBUTE_NAME, optionExc.val, //
|
||||||
ENABLED_ATTRIBUTE_NAME,
|
ENABLED_ATTRIBUTE_NAME,
|
||||||
optionExc.equals(DebugFilterExecutionOption.DEBUG_FILTER_BREAK)), "Refreshed");
|
optionExc.equals(DebugFilterExecutionOption.DEBUG_FILTER_BREAK)), "Refreshed");
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,6 +78,7 @@ public class DbgModelTargetProcessLaunchConnectorImpl extends DbgModelTargetObje
|
||||||
map.put("cf", cf);
|
map.put("cf", cf);
|
||||||
map.put("ef", ef);
|
map.put("ef", ef);
|
||||||
map.put("vf", vf);
|
map.put("vf", vf);
|
||||||
|
// Innocuous comment: up-up-down-down-left-right-left-right-B-A
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,11 +92,4 @@ public class DbgModelTargetProcessLaunchConnectorImpl extends DbgModelTargetObje
|
||||||
return getManager().launch(args);
|
return getManager().launch(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
// public CompletableFuture<Void> launch(List<String> args) {
|
|
||||||
// return AsyncUtils.sequence(TypeSpec.VOID).then(seq -> {
|
|
||||||
// getManager().launch(args).handle(seq::nextIgnore);
|
|
||||||
// }).finish().exceptionally((exc) -> {
|
|
||||||
// throw new DebuggerUserException("Launch failed for " + args);
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,6 @@ import ghidra.dbg.error.DebuggerMemoryAccessException;
|
||||||
import ghidra.dbg.target.*;
|
import ghidra.dbg.target.*;
|
||||||
import ghidra.dbg.target.TargetConsole.Channel;
|
import ghidra.dbg.target.TargetConsole.Channel;
|
||||||
import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState;
|
import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState;
|
||||||
import ghidra.dbg.target.TargetLauncher.TargetCmdLineLauncher;
|
|
||||||
import ghidra.dbg.target.TargetMethod.ParameterDescription;
|
import ghidra.dbg.target.TargetMethod.ParameterDescription;
|
||||||
import ghidra.dbg.target.TargetSteppable.TargetStepKind;
|
import ghidra.dbg.target.TargetSteppable.TargetStepKind;
|
||||||
import ghidra.dbg.util.DebuggerCallbackReorderer;
|
import ghidra.dbg.util.DebuggerCallbackReorderer;
|
||||||
|
@ -1335,38 +1334,32 @@ public class DebuggerObjectsProvider extends ComponentProviderAdapter
|
||||||
if (currentProgram == null) {
|
if (currentProgram == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
performAction(context, true, TargetLauncher.class, launcher -> {
|
performLaunchAction(context, false);
|
||||||
// TODO: A generic or pluggable way of deriving the launch arguments
|
|
||||||
return launcher.launch(Map.of(
|
|
||||||
TargetCmdLineLauncher.CMDLINE_ARGS_NAME, currentProgram.getExecutablePath()));
|
|
||||||
}, "Couldn't launch");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void performLaunch(ActionContext context) {
|
public void performLaunch(ActionContext context) {
|
||||||
performAction(context, true, TargetLauncher.class, launcher -> {
|
performLaunchAction(context, true);
|
||||||
|
}
|
||||||
|
|
||||||
Map<String, ?> args = launchOffer.getLauncherArgs(launcher, true);
|
private void performLaunchAction(ActionContext context, boolean p) {
|
||||||
|
performAction(context, true, TargetLauncher.class, launcher -> {
|
||||||
|
var locals = new Object() {
|
||||||
|
boolean prompt = p;
|
||||||
|
};
|
||||||
|
return AsyncUtils.loop(TypeSpec.VOID, (loop) -> {
|
||||||
|
Map<String, ?> args = launchOffer.getLauncherArgs(launcher, locals.prompt);
|
||||||
if (args == null) {
|
if (args == null) {
|
||||||
// Cancelled
|
// Cancelled
|
||||||
return AsyncUtils.NIL;
|
loop.exit();
|
||||||
}
|
}
|
||||||
return launcher.launch(args);
|
else {
|
||||||
/*
|
launcher.launch(args).thenAccept(loop::exit).exceptionally(ex -> {
|
||||||
String argsKey = TargetCmdLineLauncher.CMDLINE_ARGS_NAME;
|
loop.repeat();
|
||||||
String path = (currentProgram != null) ? currentProgram.getExecutablePath() : null;
|
return null;
|
||||||
launchDialog.setCurrentContext(path);
|
});
|
||||||
String cmdlineArgs = launchDialog.getMemorizedArgument(argsKey, String.class);
|
|
||||||
if (cmdlineArgs == null) {
|
|
||||||
cmdlineArgs = path;
|
|
||||||
launchDialog.setMemorizedArgument(argsKey, String.class,
|
|
||||||
cmdlineArgs);
|
|
||||||
}
|
}
|
||||||
Map<String, ?> args = launchDialog.promptArguments(launcher.getParameters());
|
locals.prompt = true;
|
||||||
if (args == null) {
|
});
|
||||||
return AsyncUtils.NIL;
|
|
||||||
}
|
|
||||||
return launcher.launch(args);
|
|
||||||
*/
|
|
||||||
}, "Couldn't launch");
|
}, "Couldn't launch");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,9 +19,7 @@ import java.awt.BorderLayout;
|
||||||
import java.awt.FlowLayout;
|
import java.awt.FlowLayout;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.beans.*;
|
import java.beans.*;
|
||||||
import java.util.HashMap;
|
import java.util.*;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.border.EmptyBorder;
|
import javax.swing.border.EmptyBorder;
|
||||||
|
@ -32,6 +30,7 @@ import org.apache.commons.lang3.tuple.MutablePair;
|
||||||
import org.jdom.Element;
|
import org.jdom.Element;
|
||||||
|
|
||||||
import docking.DialogComponentProvider;
|
import docking.DialogComponentProvider;
|
||||||
|
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
|
||||||
import ghidra.app.plugin.core.debug.utils.MiscellaneousUtils;
|
import ghidra.app.plugin.core.debug.utils.MiscellaneousUtils;
|
||||||
import ghidra.dbg.target.TargetMethod;
|
import ghidra.dbg.target.TargetMethod;
|
||||||
import ghidra.dbg.target.TargetMethod.ParameterDescription;
|
import ghidra.dbg.target.TargetMethod.ParameterDescription;
|
||||||
|
@ -92,6 +91,8 @@ public class DebuggerMethodInvocationDialog extends DialogComponentProvider
|
||||||
private PairLayout layout;
|
private PairLayout layout;
|
||||||
|
|
||||||
protected JButton invokeButton;
|
protected JButton invokeButton;
|
||||||
|
protected JButton resetButton;
|
||||||
|
protected boolean resetRequested;
|
||||||
|
|
||||||
private final PluginTool tool;
|
private final PluginTool tool;
|
||||||
private Map<String, ParameterDescription<?>> parameters;
|
private Map<String, ParameterDescription<?>> parameters;
|
||||||
|
@ -105,7 +106,7 @@ public class DebuggerMethodInvocationDialog extends DialogComponentProvider
|
||||||
super(title, true, false, true, false);
|
super(title, true, false, true, false);
|
||||||
this.tool = tool;
|
this.tool = tool;
|
||||||
|
|
||||||
populateComponents(buttonText, buttonIcon);
|
populateComponents(buttonText, buttonIcon, "Reset", DebuggerResources.ICON_REFRESH);
|
||||||
setRememberSize(false);
|
setRememberSize(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,7 +127,8 @@ public class DebuggerMethodInvocationDialog extends DialogComponentProvider
|
||||||
populateOptions();
|
populateOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void populateComponents(String buttonText, Icon buttonIcon) {
|
private void populateComponents(String buttonText, Icon buttonIcon,
|
||||||
|
String resetText, Icon resetIcon) {
|
||||||
panel = new JPanel(new BorderLayout());
|
panel = new JPanel(new BorderLayout());
|
||||||
panel.setBorder(new EmptyBorder(10, 10, 10, 10));
|
panel.setBorder(new EmptyBorder(10, 10, 10, 10));
|
||||||
|
|
||||||
|
@ -144,19 +146,31 @@ public class DebuggerMethodInvocationDialog extends DialogComponentProvider
|
||||||
|
|
||||||
invokeButton = new JButton(buttonText, buttonIcon);
|
invokeButton = new JButton(buttonText, buttonIcon);
|
||||||
addButton(invokeButton);
|
addButton(invokeButton);
|
||||||
|
resetButton = new JButton(resetText, resetIcon);
|
||||||
|
addButton(resetButton);
|
||||||
addCancelButton();
|
addCancelButton();
|
||||||
|
|
||||||
invokeButton.addActionListener(this::invoke);
|
invokeButton.addActionListener(this::invoke);
|
||||||
|
resetButton.addActionListener(this::reset);
|
||||||
|
resetRequested = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void cancelCallback() {
|
protected void cancelCallback() {
|
||||||
this.arguments = null;
|
this.arguments = null;
|
||||||
|
this.resetRequested = false;
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void invoke(ActionEvent evt) {
|
private void invoke(ActionEvent evt) {
|
||||||
this.arguments = TargetMethod.validateArguments(parameters, collectArguments(), false);
|
this.arguments = TargetMethod.validateArguments(parameters, collectArguments(), false);
|
||||||
|
this.resetRequested = false;
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reset(ActionEvent evt) {
|
||||||
|
this.arguments = new LinkedHashMap<>();
|
||||||
|
this.resetRequested = true;
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,7 +189,8 @@ public class DebuggerMethodInvocationDialog extends DialogComponentProvider
|
||||||
Msg.warn(this, "No editor for " + type + "? Trying String instead");
|
Msg.warn(this, "No editor for " + type + "? Trying String instead");
|
||||||
editor = PropertyEditorManager.findEditor(String.class);
|
editor = PropertyEditorManager.findEditor(String.class);
|
||||||
}
|
}
|
||||||
editor.setValue(computeMemorizedValue(param));
|
Object val = computeMemorizedValue(param);
|
||||||
|
editor.setValue(val);
|
||||||
editor.addPropertyChangeListener(this);
|
editor.addPropertyChangeListener(this);
|
||||||
pairPanel.add(MiscellaneousUtils.getEditorComponent(editor));
|
pairPanel.add(MiscellaneousUtils.getEditorComponent(editor));
|
||||||
// TODO: How to handle parameter with choices?
|
// TODO: How to handle parameter with choices?
|
||||||
|
@ -184,10 +199,14 @@ public class DebuggerMethodInvocationDialog extends DialogComponentProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Map<String, ?> collectArguments() {
|
protected Map<String, ?> collectArguments() {
|
||||||
return paramEditors.keySet()
|
Map<String, Object> map = new LinkedHashMap<>();
|
||||||
.stream()
|
for (ParameterDescription<?> param : paramEditors.keySet()) {
|
||||||
.collect(Collectors.toMap(param -> param.name,
|
Object val = memorized.get(NameTypePair.fromParameter(param));
|
||||||
param -> memorized.get(NameTypePair.fromParameter(param))));
|
if (val != null) {
|
||||||
|
map.put(param.name, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, ?> getArguments() {
|
public Map<String, ?> getArguments() {
|
||||||
|
@ -196,6 +215,9 @@ public class DebuggerMethodInvocationDialog extends DialogComponentProvider
|
||||||
|
|
||||||
public <T> void setMemorizedArgument(String name, Class<T> type, T value) {
|
public <T> void setMemorizedArgument(String name, Class<T> type, T value) {
|
||||||
//name = addContext(name, currentContext);
|
//name = addContext(name, currentContext);
|
||||||
|
if (value == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
memorized.put(new NameTypePair(name, type), value);
|
memorized.put(new NameTypePair(name, type), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,4 +261,8 @@ public class DebuggerMethodInvocationDialog extends DialogComponentProvider
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isResetRequested() {
|
||||||
|
return resetRequested;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,14 @@ import ghidra.util.classfinder.ClassSearcher;
|
||||||
import ghidra.util.datastruct.CollectionChangeListener;
|
import ghidra.util.datastruct.CollectionChangeListener;
|
||||||
import ghidra.util.datastruct.ListenerSet;
|
import ghidra.util.datastruct.ListenerSet;
|
||||||
|
|
||||||
@PluginInfo(shortDescription = "Debugger models manager service", description = "Manage debug sessions, connections, and trace recording", category = PluginCategoryNames.DEBUGGER, packageName = DebuggerPluginPackage.NAME, status = PluginStatus.HIDDEN, servicesRequired = {}, servicesProvided = {
|
@PluginInfo(
|
||||||
|
shortDescription = "Debugger models manager service",
|
||||||
|
description = "Manage debug sessions, connections, and trace recording",
|
||||||
|
category = PluginCategoryNames.DEBUGGER,
|
||||||
|
packageName = DebuggerPluginPackage.NAME,
|
||||||
|
status = PluginStatus.HIDDEN,
|
||||||
|
servicesRequired = {},
|
||||||
|
servicesProvided = {
|
||||||
DebuggerModelService.class, })
|
DebuggerModelService.class, })
|
||||||
public class DebuggerModelServicePlugin extends Plugin
|
public class DebuggerModelServicePlugin extends Plugin
|
||||||
implements DebuggerModelServiceInternal, ApplicationLevelOnlyPlugin {
|
implements DebuggerModelServiceInternal, ApplicationLevelOnlyPlugin {
|
||||||
|
@ -239,7 +246,10 @@ public class DebuggerModelServicePlugin extends Plugin
|
||||||
}
|
}
|
||||||
model.addModelListener(forRemovalAndFocusListener);
|
model.addModelListener(forRemovalAndFocusListener);
|
||||||
TargetObject root = model.getModelRoot();
|
TargetObject root = model.getModelRoot();
|
||||||
if (!root.isValid()) {
|
// root == null, probably means we're between model construction
|
||||||
|
// and root construction, but the model was not closed, so no need
|
||||||
|
// to invalidate
|
||||||
|
if (root != null && !root.isValid()) {
|
||||||
forRemovalAndFocusListener.invalidated(root, root,
|
forRemovalAndFocusListener.invalidated(root, root,
|
||||||
"Invalidated before or during add to service");
|
"Invalidated before or during add to service");
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,7 @@ import ghidra.app.plugin.core.debug.gui.DebuggerResources.DebugProgramAction;
|
||||||
import ghidra.app.plugin.core.debug.gui.DebuggerResources.DisconnectAllAction;
|
import ghidra.app.plugin.core.debug.gui.DebuggerResources.DisconnectAllAction;
|
||||||
import ghidra.app.plugin.core.debug.mapping.DebuggerTargetTraceMapper;
|
import ghidra.app.plugin.core.debug.mapping.DebuggerTargetTraceMapper;
|
||||||
import ghidra.app.plugin.core.debug.service.model.launch.DebuggerProgramLaunchOffer;
|
import ghidra.app.plugin.core.debug.service.model.launch.DebuggerProgramLaunchOffer;
|
||||||
|
import ghidra.app.plugin.core.debug.service.model.launch.DebuggerProgramLaunchOffer.PromptMode;
|
||||||
import ghidra.app.plugin.core.debug.utils.BackgroundUtils;
|
import ghidra.app.plugin.core.debug.utils.BackgroundUtils;
|
||||||
import ghidra.app.services.*;
|
import ghidra.app.services.*;
|
||||||
import ghidra.async.AsyncUtils;
|
import ghidra.async.AsyncUtils;
|
||||||
|
@ -88,7 +89,7 @@ public class DebuggerModelServiceProxyPlugin extends Plugin
|
||||||
new DebuggerProgramLaunchOffer() {
|
new DebuggerProgramLaunchOffer() {
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<LaunchResult> launchProgram(TaskMonitor monitor,
|
public CompletableFuture<LaunchResult> launchProgram(TaskMonitor monitor,
|
||||||
boolean prompt, LaunchConfigurator configurator) {
|
PromptMode prompt, LaunchConfigurator configurator) {
|
||||||
throw new AssertionError("Who clicked me?");
|
throw new AssertionError("Who clicked me?");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -330,7 +331,8 @@ public class DebuggerModelServiceProxyPlugin extends Plugin
|
||||||
return offers.sorted(Comparator.comparingInt(o -> -mrl.indexOf(o.getConfigName())));
|
return offers.sorted(Comparator.comparingInt(o -> -mrl.indexOf(o.getConfigName())));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void debugProgram(DebuggerProgramLaunchOffer offer, Program program, boolean prompt) {
|
private void debugProgram(DebuggerProgramLaunchOffer offer, Program program,
|
||||||
|
PromptMode prompt) {
|
||||||
BackgroundUtils.asyncModal(tool, offer.getButtonTitle(), true, true, m -> {
|
BackgroundUtils.asyncModal(tool, offer.getButtonTitle(), true, true, m -> {
|
||||||
List<String> recent = new ArrayList<>(readMostRecentLaunches(program));
|
List<String> recent = new ArrayList<>(readMostRecentLaunches(program));
|
||||||
recent.remove(offer.getConfigName());
|
recent.remove(offer.getConfigName());
|
||||||
|
@ -360,7 +362,7 @@ public class DebuggerModelServiceProxyPlugin extends Plugin
|
||||||
if (offer == null || program == null) {
|
if (offer == null || program == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
debugProgram(offer, program, false);
|
debugProgram(offer, program, PromptMode.ON_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void debugProgramStateActivated(ActionState<DebuggerProgramLaunchOffer> offer,
|
private void debugProgramStateActivated(ActionState<DebuggerProgramLaunchOffer> offer,
|
||||||
|
@ -375,7 +377,7 @@ public class DebuggerModelServiceProxyPlugin extends Plugin
|
||||||
if (program == null) {
|
if (program == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
debugProgram(offer, program, true);
|
debugProgram(offer, program, PromptMode.ALWAYS);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateActionDebugProgram() {
|
private void updateActionDebugProgram() {
|
||||||
|
|
|
@ -15,8 +15,11 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.debug.service.model.launch;
|
package ghidra.app.plugin.core.debug.service.model.launch;
|
||||||
|
|
||||||
|
import static ghidra.async.AsyncUtils.*;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.Map.Entry;
|
||||||
import java.util.concurrent.CancellationException;
|
import java.util.concurrent.CancellationException;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
@ -267,7 +270,12 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg
|
||||||
if (program == null) {
|
if (program == null) {
|
||||||
return Map.of();
|
return Map.of();
|
||||||
}
|
}
|
||||||
return Map.of(TargetCmdLineLauncher.CMDLINE_ARGS_NAME, program.getExecutablePath());
|
Map<String, Object> map = new LinkedHashMap<String, Object>();
|
||||||
|
for (Entry<String, ParameterDescription<?>> entry : params.entrySet()) {
|
||||||
|
map.put(entry.getKey(), entry.getValue().defaultValue);
|
||||||
|
}
|
||||||
|
map.put(TargetCmdLineLauncher.CMDLINE_ARGS_NAME, program.getExecutablePath());
|
||||||
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -282,12 +290,16 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg
|
||||||
DebuggerMethodInvocationDialog dialog =
|
DebuggerMethodInvocationDialog dialog =
|
||||||
new DebuggerMethodInvocationDialog(tool, getButtonTitle(), "Launch", getIcon());
|
new DebuggerMethodInvocationDialog(tool, getButtonTitle(), "Launch", getIcon());
|
||||||
// NB. Do not invoke read/writeConfigState
|
// NB. Do not invoke read/writeConfigState
|
||||||
Map<String, ?> args = configurator.configureLauncher(launcher,
|
Map<String, ?> args;
|
||||||
|
boolean reset = false;
|
||||||
|
do {
|
||||||
|
args = configurator.configureLauncher(launcher,
|
||||||
loadLastLauncherArgs(launcher, true), RelPrompt.BEFORE);
|
loadLastLauncherArgs(launcher, true), RelPrompt.BEFORE);
|
||||||
for (ParameterDescription<?> param : params.values()) {
|
for (ParameterDescription<?> param : params.values()) {
|
||||||
Object val = args.get(param.name);
|
Object val = args.get(param.name);
|
||||||
if (val != null) {
|
if (val != null) {
|
||||||
dialog.setMemorizedArgument(param.name, param.type.asSubclass(Object.class), val);
|
dialog.setMemorizedArgument(param.name, param.type.asSubclass(Object.class),
|
||||||
|
val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
args = dialog.promptArguments(params);
|
args = dialog.promptArguments(params);
|
||||||
|
@ -295,7 +307,13 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg
|
||||||
// Cancelled
|
// Cancelled
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
reset = dialog.isResetRequested();
|
||||||
|
if (reset) {
|
||||||
|
args = generateDefaultLauncherArgs(params);
|
||||||
|
}
|
||||||
saveLauncherArgs(args, params);
|
saveLauncherArgs(args, params);
|
||||||
|
}
|
||||||
|
while (reset);
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -328,14 +346,17 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg
|
||||||
try {
|
try {
|
||||||
Element element = XmlUtilities.fromString(property);
|
Element element = XmlUtilities.fromString(property);
|
||||||
SaveState state = new SaveState(element);
|
SaveState state = new SaveState(element);
|
||||||
|
List<String> names = List.of(state.getNames());
|
||||||
Map<String, Object> args = new LinkedHashMap<>();
|
Map<String, Object> args = new LinkedHashMap<>();
|
||||||
for (ParameterDescription<?> param : params.values()) {
|
for (ParameterDescription<?> param : params.values()) {
|
||||||
|
if (names.contains(param.name)) {
|
||||||
Object configState =
|
Object configState =
|
||||||
ConfigStateField.getState(state, param.type, param.name);
|
ConfigStateField.getState(state, param.type, param.name);
|
||||||
if (configState != null) {
|
if (configState != null) {
|
||||||
args.put(param.name, configState);
|
args.put(param.name, configState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (!args.isEmpty()) {
|
if (!args.isEmpty()) {
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
@ -347,7 +368,7 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg
|
||||||
e);
|
e);
|
||||||
}
|
}
|
||||||
Msg.error(this,
|
Msg.error(this,
|
||||||
"Saved launcher args are corrup, or launcher parameters changed. Defaulting.",
|
"Saved launcher args are corrupt, or launcher parameters changed. Defaulting.",
|
||||||
e);
|
e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -463,12 +484,14 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg
|
||||||
|
|
||||||
// Eww.
|
// Eww.
|
||||||
protected CompletableFuture<Void> launch(TargetLauncher launcher,
|
protected CompletableFuture<Void> launch(TargetLauncher launcher,
|
||||||
boolean prompt, LaunchConfigurator configurator) {
|
boolean prompt, LaunchConfigurator configurator, TaskMonitor monitor) {
|
||||||
Map<String, ?> args = getLauncherArgs(launcher, prompt, configurator);
|
Map<String, ?> args = getLauncherArgs(launcher, prompt, configurator);
|
||||||
if (args == null) {
|
if (args == null) {
|
||||||
throw new CancellationException();
|
throw new CancellationException();
|
||||||
}
|
}
|
||||||
return launcher.launch(args);
|
return AsyncTimer.DEFAULT_TIMER.mark()
|
||||||
|
.timeOut(
|
||||||
|
launcher.launch(args), getTimeoutMillis(), () -> onTimedOutLaunch(monitor));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void checkCancelled(TaskMonitor monitor) {
|
protected void checkCancelled(TaskMonitor monitor) {
|
||||||
|
@ -560,7 +583,7 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<LaunchResult> launchProgram(TaskMonitor monitor, boolean prompt,
|
public CompletableFuture<LaunchResult> launchProgram(TaskMonitor monitor, PromptMode mode,
|
||||||
LaunchConfigurator configurator) {
|
LaunchConfigurator configurator) {
|
||||||
DebuggerModelService service = tool.getService(DebuggerModelService.class);
|
DebuggerModelService service = tool.getService(DebuggerModelService.class);
|
||||||
DebuggerStaticMappingService mappingService =
|
DebuggerStaticMappingService mappingService =
|
||||||
|
@ -573,12 +596,13 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg
|
||||||
TargetObject target;
|
TargetObject target;
|
||||||
TraceRecorder recorder;
|
TraceRecorder recorder;
|
||||||
Throwable exception;
|
Throwable exception;
|
||||||
|
boolean prompt = mode == PromptMode.ALWAYS;
|
||||||
|
|
||||||
LaunchResult getResult() {
|
LaunchResult getResult() {
|
||||||
return new LaunchResult(model, target, recorder, exception);
|
return new LaunchResult(model, target, recorder, exception);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return connect(service, prompt, configurator).thenCompose(m -> {
|
return connect(service, locals.prompt, configurator).thenCompose(m -> {
|
||||||
checkCancelled(monitor);
|
checkCancelled(monitor);
|
||||||
locals.model = m;
|
locals.model = m;
|
||||||
monitor.incrementProgress(1);
|
monitor.incrementProgress(1);
|
||||||
|
@ -591,12 +615,14 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg
|
||||||
monitor.incrementProgress(1);
|
monitor.incrementProgress(1);
|
||||||
monitor.setMessage("Launching");
|
monitor.setMessage("Launching");
|
||||||
locals.futureTarget = listenForTarget(l.getModel());
|
locals.futureTarget = listenForTarget(l.getModel());
|
||||||
if (prompt) {
|
return loop(TypeSpec.VOID, (loop) -> {
|
||||||
return launch(l, true, configurator);
|
launch(l, locals.prompt, configurator, monitor).thenAccept(loop::exit)
|
||||||
}
|
.exceptionally(ex -> {
|
||||||
return AsyncTimer.DEFAULT_TIMER.mark()
|
loop.repeat();
|
||||||
.timeOut(launch(l, false, configurator), getTimeoutMillis(),
|
return null;
|
||||||
() -> onTimedOutLaunch(monitor));
|
});
|
||||||
|
locals.prompt = mode != PromptMode.NEVER;
|
||||||
|
});
|
||||||
}).thenCompose(__ -> {
|
}).thenCompose(__ -> {
|
||||||
checkCancelled(monitor);
|
checkCancelled(monitor);
|
||||||
monitor.incrementProgress(1);
|
monitor.incrementProgress(1);
|
||||||
|
@ -636,4 +662,5 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg
|
||||||
return locals.getResult();
|
return locals.getResult();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,6 +82,21 @@ public interface DebuggerProgramLaunchOffer {
|
||||||
AFTER;
|
AFTER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum PromptMode {
|
||||||
|
/**
|
||||||
|
* The user is always prompted for parameters.
|
||||||
|
*/
|
||||||
|
ALWAYS,
|
||||||
|
/**
|
||||||
|
* The user is never prompted for parameters.
|
||||||
|
*/
|
||||||
|
NEVER,
|
||||||
|
/**
|
||||||
|
* The user is prompted after an error.
|
||||||
|
*/
|
||||||
|
ON_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callbacks for custom configuration when launching a program
|
* Callbacks for custom configuration when launching a program
|
||||||
*/
|
*/
|
||||||
|
@ -118,7 +133,7 @@ public interface DebuggerProgramLaunchOffer {
|
||||||
* @param configurator the configuration callbacks
|
* @param configurator the configuration callbacks
|
||||||
* @return a future which completes when the program is launched
|
* @return a future which completes when the program is launched
|
||||||
*/
|
*/
|
||||||
CompletableFuture<LaunchResult> launchProgram(TaskMonitor monitor, boolean prompt,
|
CompletableFuture<LaunchResult> launchProgram(TaskMonitor monitor, PromptMode prompt,
|
||||||
LaunchConfigurator configurator);
|
LaunchConfigurator configurator);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -128,7 +143,7 @@ public interface DebuggerProgramLaunchOffer {
|
||||||
* @param prompt if the user should be prompted to confirm launch parameters
|
* @param prompt if the user should be prompted to confirm launch parameters
|
||||||
* @return a future which completes when the program is launched
|
* @return a future which completes when the program is launched
|
||||||
*/
|
*/
|
||||||
default CompletableFuture<LaunchResult> launchProgram(TaskMonitor monitor, boolean prompt) {
|
default CompletableFuture<LaunchResult> launchProgram(TaskMonitor monitor, PromptMode prompt) {
|
||||||
return launchProgram(monitor, prompt, LaunchConfigurator.NOP);
|
return launchProgram(monitor, prompt, LaunchConfigurator.NOP);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1491,7 +1491,7 @@ public interface FlatDebuggerAPI {
|
||||||
default LaunchResult launch(DebuggerProgramLaunchOffer offer, String commandLine,
|
default LaunchResult launch(DebuggerProgramLaunchOffer offer, String commandLine,
|
||||||
TaskMonitor monitor) {
|
TaskMonitor monitor) {
|
||||||
try {
|
try {
|
||||||
return waitOn(offer.launchProgram(monitor, false, new LaunchConfigurator() {
|
return waitOn(offer.launchProgram(monitor, PromptMode.NEVER, new LaunchConfigurator() {
|
||||||
@Override
|
@Override
|
||||||
public Map<String, ?> configureLauncher(TargetLauncher launcher,
|
public Map<String, ?> configureLauncher(TargetLauncher launcher,
|
||||||
Map<String, ?> arguments, RelPrompt relPrompt) {
|
Map<String, ?> arguments, RelPrompt relPrompt) {
|
||||||
|
@ -1514,7 +1514,7 @@ public interface FlatDebuggerAPI {
|
||||||
*/
|
*/
|
||||||
default LaunchResult launch(DebuggerProgramLaunchOffer offer, TaskMonitor monitor) {
|
default LaunchResult launch(DebuggerProgramLaunchOffer offer, TaskMonitor monitor) {
|
||||||
try {
|
try {
|
||||||
return waitOn(offer.launchProgram(monitor, false));
|
return waitOn(offer.launchProgram(monitor, PromptMode.NEVER));
|
||||||
}
|
}
|
||||||
catch (InterruptedException | ExecutionException | TimeoutException e) {
|
catch (InterruptedException | ExecutionException | TimeoutException e) {
|
||||||
// TODO: This is not ideal, since it's likely partially completed
|
// TODO: This is not ideal, since it's likely partially completed
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.debug.service.model;
|
package ghidra.app.plugin.core.debug.service.model;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -35,7 +35,7 @@ public class TestDebuggerProgramLaunchOpinion implements DebuggerProgramLaunchOp
|
||||||
|
|
||||||
public static class TestDebuggerProgramLaunchOffer implements DebuggerProgramLaunchOffer {
|
public static class TestDebuggerProgramLaunchOffer implements DebuggerProgramLaunchOffer {
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<LaunchResult> launchProgram(TaskMonitor monitor, boolean prompt,
|
public CompletableFuture<LaunchResult> launchProgram(TaskMonitor monitor, PromptMode prompt,
|
||||||
LaunchConfigurator configurator) {
|
LaunchConfigurator configurator) {
|
||||||
return CompletableFuture
|
return CompletableFuture
|
||||||
.completedFuture(LaunchResult.totalFailure(new AssertionError()));
|
.completedFuture(LaunchResult.totalFailure(new AssertionError()));
|
||||||
|
|
|
@ -139,4 +139,5 @@ public interface TargetLauncher extends TargetObject {
|
||||||
* @return a future which completes when the command is completed
|
* @return a future which completes when the command is completed
|
||||||
*/
|
*/
|
||||||
public CompletableFuture<Void> launch(Map<String, ?> args);
|
public CompletableFuture<Void> launch(Map<String, ?> args);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue