());
+
+ getSession().addServiceEventListener(GdbBackend.this, null);
+
+ /*
+ * This event is not consumed by any one at present, instead it's
+ * the GDBControlInitializedDMEvent that's used to indicate that GDB
+ * back end is ready for MI commands. But we still fire the event as
+ * it does no harm and may be needed sometime.... 09/29/2008
+ *
+ * We send the event in the register step because that is when other
+ * services have access to it.
+ */
+ getSession().dispatchEvent(new BackendStateChangedEvent(getSession().getId(), getId(), State.STARTED),
+ getProperties());
+
+ requestMonitor.done();
+ }
+
+ @Override
+ protected void shutdown(RequestMonitor requestMonitor) {
+ unregister();
+ getSession().removeServiceEventListener(GdbBackend.this);
+ requestMonitor.done();
}
+ }
+
+ /**
+ * Monitors a system process, waiting for it to terminate, and then notifies
+ * the associated runtime process.
+ */
+ private class MonitorJob extends Job {
+ boolean fMonitorExited = false;
+ DsfRunnable fMonitorStarted;
+ Process fMonProcess;
+
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ synchronized (fMonProcess) {
+ getExecutor().submit(fMonitorStarted);
+ try {
+ fMonProcess.waitFor();
+ fGDBExitValue = fMonProcess.exitValue();
+
+ if (Activator.getInstance().isDebugging()) {
+ System.out.println("MonitorJob.run() exitValue() " + fGDBExitValue);
+ }
+
+ if(fProcess.isAlive() && Activator.getInstance().isDebugging())
+ {
+ // Need to do this on the executor for thread-safety
+ getExecutor().submit(new DsfRunnable() {
+ @Override
+ public void run() {
+
+ if (Activator.getInstance().isDebugging()) {
+ System.out.println("MonitorJob.run() run() ");
+ }
+
+ destroy();
+ fBackendState = State.TERMINATED;
+
+ if (Activator.getInstance().isDebugging()) {
+ System.out.println(
+ "MonitorJob.run() run() dispatchEvent(BackendStateChangedEvent, TERMINATED)");
+ }
+ getSession().dispatchEvent(
+ new BackendStateChangedEvent(getSession().getId(), getId(), State.TERMINATED),
+ getProperties());
+ }
+ });
+ }
+
+ } catch (InterruptedException ie) {
+ // clear interrupted state
+ Thread.interrupted();
+ }
+
+ fMonitorExited = true;
+ }
+ return Status.OK_STATUS;
+ }
+
+ MonitorJob(Process process, DsfRunnable monitorStarted) {
+ super("GDB process monitor job."); //$NON-NLS-1$
+ fMonProcess = process;
+ fMonitorStarted = monitorStarted;
+ setSystem(true);
+ }
+
+ void kill() {
+ synchronized (fMonProcess) {
+ if (!fMonitorExited) {
+ getThread().interrupt();
+ }
+ }
+ }
+ }
+
+ /**
+ * Stores the request monitor that must be dealt with for the result of the
+ * interrupt operation. If the interrupt successfully suspends the backend,
+ * the request monitor can be retrieved and completed successfully, and then
+ * this job should be canceled. If this job is not canceled before the time
+ * is up, it will imply the interrupt did not successfully suspend the
+ * backend, and the current job will indicate this in the request monitor.
+ *
+ * The specified timeout is used to indicate how many milliseconds this job
+ * should wait for. INTERRUPT_TIMEOUT_DEFAULT indicates to use the default
+ * of 5 seconds. The default is also use if the timeout value is 0 or
+ * negative.
+ *
+ * @since 3.0
+ */
+ protected class MonitorInterruptJob extends Job {
+ // Bug 310274. Until we have a preference to configure timeouts,
+ // we need a large enough default timeout to accommodate slow
+ // remote sessions.
+ private final static int TIMEOUT_DEFAULT_VALUE = 5000;
+ private final RequestMonitor fRequestMonitor;
+
+ public MonitorInterruptJob(int timeout, RequestMonitor rm) {
+ super("Interrupt monitor job."); //$NON-NLS-1$
+ setSystem(true);
+ fRequestMonitor = rm;
+
+ if (timeout == INTERRUPT_TIMEOUT_DEFAULT || timeout <= 0) {
+ timeout = TIMEOUT_DEFAULT_VALUE; // default of 5 seconds
+ }
+
+ schedule(timeout);
+ }
+
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ getExecutor().submit(new DsfRunnable() {
+ @Override
+ public void run() {
+ fInterruptFailedJob = null;
+ fRequestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID,
+ IDsfStatusConstants.REQUEST_FAILED, "Interrupt failed.", null)); //$NON-NLS-1$
+ fRequestMonitor.done();
+ }
+ });
+ return Status.OK_STATUS;
+ }
+
+ public RequestMonitor getRequestMonitor() {
+ return fRequestMonitor;
+ }
+ }
+
+ /**
+ * We use this handler to determine if the SIGINT we sent to GDB has been
+ * effective. We must listen for an MI event and not a higher-level
+ * ISuspendedEvent. The reason is that some ISuspendedEvent are not sent
+ * when the target stops, in cases where we don't want to views to update.
+ * For example, if we want to interrupt the target to set a breakpoint, this
+ * interruption is done silently; we will receive the MI event though.
+ *
+ *
+ * Though we send a SIGINT, we may not specifically get an MISignalEvent.
+ * Typically we will, but not always, so wait for an MIStoppedEvent. See
+ * https://bugs.eclipse.org/bugs/show_bug.cgi?id=305178#c21
+ *
+ * @since 3.0
+ *
+ */
+ @DsfServiceEventHandler
+ public void eventDispatched(final MIStoppedEvent e) {
+ if (fInterruptFailedJob != null) {
+ if (fInterruptFailedJob.cancel()) {
+ fInterruptFailedJob.getRequestMonitor().done();
+ }
+ fInterruptFailedJob = null;
+ }
+ }
+
+ /**
+ * Safety net, in case the GDB client does not exit on command.
+ *
+ * The event is created and triggered by Process.terminate().
+ *
+ * @param e
+ */
+ @DsfServiceEventHandler
+ public void eventDispatched(final ProcessStateChangedEvent e) {
if (Activator.getInstance().isDebugging()) {
- System.out.println("openocd.GdbBackend.getGDBWorkingDirectory() " + path);
+ System.out.println("GnuMcuGdbBackend.eventDispatched() " + e);
+ }
+
+ // When the process is terminated, also terminate the backend.
+ if (e.getState() == State.TERMINATED && e.getSessionId().equals(getSession().getId())
+ && getState() == State.STARTED) {
+
+ destroy();
}
- return path;
}
// ------------------------------------------------------------------------
diff --git a/bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java b/bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java
index 8749df392..4b2583375 100644
--- a/bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java
+++ b/bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java
@@ -14,7 +14,9 @@
package com.espressif.idf.debug.gdbjtag.openocd.dsf;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
@@ -32,15 +34,14 @@
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.debug.core.model.ISourceLocator;
-import org.eclipse.debug.internal.core.LaunchConfigurationWorkingCopy;
import org.eclipse.embedcdt.debug.gdbjtag.core.dsf.GnuMcuLaunch;
-import com.espressif.idf.core.util.PortChecker;
import com.espressif.idf.debug.gdbjtag.openocd.Activator;
import com.espressif.idf.debug.gdbjtag.openocd.Configuration;
import com.espressif.idf.debug.gdbjtag.openocd.ConfigurationAttributes;
@@ -57,6 +58,11 @@ public class Launch extends GnuMcuLaunch
private DsfSession fSession;
private DsfServicesTracker fTracker;
private DefaultDsfExecutor fExecutor;
+ private IProcess openOcdServerProcess;
+ private IProcess gdbIProcess;
+
+ private static final String SERVER_PROC_KEY = "SERVER_PROC";
+ private static final String GDB_PROC_KEY = "GDB_PROC";
// ------------------------------------------------------------------------
@@ -100,6 +106,15 @@ public void run()
// fireChanged();
}
};
+
+ try
+ {
+ cleanUpOldLaunchProcesses();
+ }
+ catch (CoreException e)
+ {
+ e.printStackTrace();
+ }
// Invoke the execution code and block waiting for the result.
try
@@ -116,6 +131,7 @@ public void run()
new Status(IStatus.ERROR, Activator.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR,
"Error initializing launch", e); //$NON-NLS-1$
}
+
}
@Override
@@ -147,9 +163,10 @@ protected void provideDefaults(ILaunchConfigurationWorkingCopy config) throws Co
fDefaultPreferences.getGdbClientExecutable());
}
- int availableRemotePort = PortChecker.getAvailablePort(config.getAttribute(IGDBJtagConstants.ATTR_PORT_NUMBER,
- DefaultPreferences.GDB_SERVER_GDB_PORT_NUMBER_DEFAULT));
- config.setAttribute(IGDBJtagConstants.ATTR_PORT_NUMBER, availableRemotePort);
+ if (Configuration.getDoStartGdbServer(config))
+ {
+ config.setAttribute(IGDBJtagConstants.ATTR_PORT_NUMBER, DefaultPreferences.GDB_SERVER_GDB_PORT_NUMBER_DEFAULT);
+ }
config.setAttribute(DebugPlugin.ATTR_PROCESS_FACTORY_ID, CustomIdfProcessFactory.ID);
}
@@ -164,16 +181,14 @@ public void initializeServerConsole(IProgressMonitor monitor) throws CoreExcepti
System.out.println("openocd.Launch.initializeServerConsole()");
}
- IProcess newProcess;
boolean doAddServerConsole = Configuration.getDoAddServerConsole(fConfig);
if (doAddServerConsole)
{
// Add the GDB server process to the launch tree
- newProcess = addServerProcess(Configuration.getGdbServerCommandName(fConfig));
- newProcess.setAttribute(IProcess.ATTR_CMDLINE, Configuration.getGdbServerCommandLine(fConfig));
-
+ openOcdServerProcess = addServerProcess(Configuration.getGdbServerCommandName(fConfig));
+ LaunchProcessDictionary.getInstance().addProcessToDictionary(getLaunchConfiguration().getName(), SERVER_PROC_KEY, openOcdServerProcess);
monitor.worked(1);
}
}
@@ -186,12 +201,11 @@ public void initializeConsoles(IProgressMonitor monitor) throws CoreException
System.out.println("openocd.Launch.initializeConsoles()");
}
- IProcess newProcess;
{
// Add the GDB client process to the launch tree.
- newProcess = addClientProcess(Configuration.getGdbClientCommandName(fConfig));
- newProcess.setAttribute(IProcess.ATTR_CMDLINE, Configuration.getGdbClientCommandLine(fConfig));
-
+ gdbIProcess = addClientProcess(Configuration.getGdbClientCommandName(fConfig));
+ gdbIProcess.setAttribute(IProcess.ATTR_CMDLINE, Configuration.getGdbClientCommandLine(fConfig));
+ LaunchProcessDictionary.getInstance().addProcessToDictionary(getLaunchConfiguration().getName(), GDB_PROC_KEY, gdbIProcess);
monitor.worked(1);
}
}
@@ -245,4 +259,53 @@ public Process call() throws CoreException
}
// ------------------------------------------------------------------------
+
+ @Override
+ public void terminate() throws DebugException
+ {
+ super.terminate();
+
+ LaunchProcessDictionary.getInstance().killAllProcessesInLaunch(getLaunchConfiguration().getName());
+ }
+
+ @Override
+ public boolean canDisconnect()
+ {
+ return true;
+ }
+
+ @Override
+ public boolean canTerminate()
+ {
+ return true;
+ }
+
+
+ @Override
+ public IProcess[] getProcesses()
+ {
+ List processes = new ArrayList<>();
+ if (openOcdServerProcess != null) {
+ processes.add(openOcdServerProcess);
+ }
+ if (gdbIProcess != null) {
+ processes.add(gdbIProcess);
+ }
+ return processes.toArray(new IProcess[0]);
+ }
+
+ private void cleanUpOldLaunchProcesses() throws CoreException
+ {
+ IProcess serverIProcess = LaunchProcessDictionary.getInstance().getProcessFromDictionary(getLaunchConfiguration().getName(), SERVER_PROC_KEY);
+ if (serverIProcess != null && !serverIProcess.isTerminated())
+ {
+ serverIProcess.terminate();
+ }
+
+ IProcess gdbIProcess = LaunchProcessDictionary.getInstance().getProcessFromDictionary(getLaunchConfiguration().getName(), GDB_PROC_KEY);
+ if(gdbIProcess != null && !gdbIProcess.isTerminated())
+ {
+ gdbIProcess.terminate();
+ }
+ }
}
diff --git a/bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/LaunchConfigurationDelegate.java b/bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/LaunchConfigurationDelegate.java
index 69642ffb9..5f59eea21 100644
--- a/bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/LaunchConfigurationDelegate.java
+++ b/bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/LaunchConfigurationDelegate.java
@@ -698,7 +698,7 @@ protected void handleCompleted()
}
// --------------------------------------------------------------------
}
-
+
/**
* Perform some local validations before starting the debug session.
*/
diff --git a/bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/LaunchProcessDictionary.java b/bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/LaunchProcessDictionary.java
new file mode 100644
index 000000000..3199d8989
--- /dev/null
+++ b/bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/LaunchProcessDictionary.java
@@ -0,0 +1,73 @@
+package com.espressif.idf.debug.gdbjtag.openocd.dsf;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.model.IProcess;
+
+public class LaunchProcessDictionary
+{
+ private static LaunchProcessDictionary instance;
+
+ private Map> processDictionary;
+
+ private LaunchProcessDictionary()
+ {
+ processDictionary = new HashMap<>();
+ }
+
+ public static LaunchProcessDictionary getInstance()
+ {
+ if(instance == null)
+ instance = new LaunchProcessDictionary();
+
+ return instance;
+ }
+
+ public void addProcessToDictionary(String launchName, String procName, IProcess process)
+ {
+ if (!processDictionary.containsKey(launchName))
+ {
+ Map processMap = new HashMap<>();
+ processMap.put(procName, process);
+ processDictionary.put(launchName, processMap);
+ return;
+ }
+
+ Map processMap = processDictionary.get(launchName);
+ processMap.put(procName, process);
+ processDictionary.put(launchName, processMap);
+ }
+
+ public IProcess getProcessFromDictionary(String launchName, String procName)
+ {
+ if (!processDictionary.containsKey(launchName))
+ {
+ return null;
+ }
+
+ return processDictionary.get(launchName).get(procName);
+ }
+
+ public void killAllProcessesInLaunch(String launchName)
+ {
+ if(!processDictionary.containsKey(launchName))
+ {
+ return;
+ }
+
+ for (IProcess process : processDictionary.get(launchName).values())
+ {
+ try
+ {
+ process.terminate();
+ }
+ catch (DebugException e)
+ {
+ e.printStackTrace();
+ }
+ }
+ }
+
+}
diff --git a/bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/process/IdfRuntimeProcess.java b/bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/process/IdfRuntimeProcess.java
index b43da1ea8..b25a451a5 100644
--- a/bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/process/IdfRuntimeProcess.java
+++ b/bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/process/IdfRuntimeProcess.java
@@ -16,6 +16,7 @@
import org.eclipse.debug.core.model.RuntimeProcess;
import org.eclipse.debug.internal.core.NullStreamsProxy;
+import com.espressif.idf.core.util.StringUtil;
import com.espressif.idf.debug.gdbjtag.openocd.dsf.process.monitors.StreamsProxy;
/**
diff --git a/bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/ui/TabDebugger.java b/bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/ui/TabDebugger.java
index 97fafb379..f854fc656 100644
--- a/bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/ui/TabDebugger.java
+++ b/bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/ui/TabDebugger.java
@@ -109,6 +109,7 @@ public class TabDebugger extends AbstractLaunchConfigurationTab
private Text fTargetIpAddress;
private Text fTargetPortNumber;
+ private Group gdbClientGroup;
protected Button fUpdateThreadlistOnSuspend;
@@ -470,6 +471,7 @@ public void widgetSelected(SelectionEvent e)
public void widgetSelected(SelectionEvent e)
{
doStartGdbServerChanged();
+ gdbClientGroup.setEnabled(!fDoStartGdbServer.getSelection());
if (fDoStartGdbServer.getSelection())
{
fTargetIpAddress.setText(DefaultPreferences.REMOTE_IP_ADDRESS_LOCALHOST);
@@ -572,16 +574,16 @@ public void modifyText(ModifyEvent e)
private void createGdbClientControls(Composite parent)
{
- Group group = new Group(parent, SWT.NONE);
+ gdbClientGroup = new Group(parent, SWT.NONE);
{
GridLayout layout = new GridLayout();
- group.setLayout(layout);
+ gdbClientGroup.setLayout(layout);
GridData gd = new GridData(GridData.FILL_HORIZONTAL);
- group.setLayoutData(gd);
- group.setText(Messages.getString("DebuggerTab.gdbSetupGroup_Text")); //$NON-NLS-1$
+ gdbClientGroup.setLayoutData(gd);
+ gdbClientGroup.setText(Messages.getString("DebuggerTab.gdbSetupGroup_Text")); //$NON-NLS-1$
}
- Composite comp = new Composite(group, SWT.NONE);
+ Composite comp = new Composite(gdbClientGroup, SWT.NONE);
{
GridLayout layout = new GridLayout();
layout.numColumns = 5;