diff --git a/bundles/com.espressif.idf.debug.gdbjtag.openocd/plugin.xml b/bundles/com.espressif.idf.debug.gdbjtag.openocd/plugin.xml
index 2fd06585f..4b2d5edf7 100644
--- a/bundles/com.espressif.idf.debug.gdbjtag.openocd/plugin.xml
+++ b/bundles/com.espressif.idf.debug.gdbjtag.openocd/plugin.xml
@@ -228,14 +228,5 @@
after="org.eclipse.debug.ui.commonTab">
-
-
-
-
diff --git a/bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/Activator.java b/bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/Activator.java
index 85ef2d3ab..e601702fc 100644
--- a/bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/Activator.java
+++ b/bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/Activator.java
@@ -42,6 +42,8 @@ public class Activator extends AbstractUIPlugin {
// The plug-in ID
public static final String PLUGIN_ID = "com.espressif.idf.debug.gdbjtag.openocd"; //$NON-NLS-1$
public static final String GDB_SERVER_LAUNCH_TIMEOUT = "fGdbServerLaunchTimeout"; //$NON-NLS-1$
+ /** Must match {@code plugin.xml} statusHandler code for {@code OpenocdStatusHandler}. */
+ public static final int OPENOCD_STARTUP_TIMEOUT_STATUS = 5012;
@Override
protected void initializeDefaultPreferences(IPreferenceStore preferenceStore)
diff --git a/bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/Configuration.java b/bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/Configuration.java
index a8cd9d3a5..5310470ed 100644
--- a/bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/Configuration.java
+++ b/bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/Configuration.java
@@ -104,13 +104,9 @@ public static String[] getGdbServerCommandLineArray(ILaunchConfiguration configu
String fmtTelnetPort = useModernSyntax ? "telnet port %d" : "telnet_port %d"; //$NON-NLS-1$ //$NON-NLS-2$
String fmtTclPort = useModernSyntax ? "tcl port %d" : "tcl_port %d"; //$NON-NLS-1$ //$NON-NLS-2$
- int port = PortChecker
- .getAvailablePort(DefaultPreferences.GDB_SERVER_GDB_PORT_NUMBER_DEFAULT);
-
- ILaunchConfigurationWorkingCopy configurationWorkingCopy = configuration.getWorkingCopy();
- configurationWorkingCopy.setAttribute(IGDBJtagConstants.ATTR_PORT_NUMBER, port);
- configurationWorkingCopy.doSave();
-
+ int port = configuration.getAttribute(IGDBJtagConstants.ATTR_PORT_NUMBER,
+ DefaultPreferences.GDB_SERVER_GDB_PORT_NUMBER_DEFAULT);
+
ILaunchTarget activeLaunchTarget = Activator.getService(ILaunchBarManager.class).getActiveLaunchTarget();
if (activeLaunchTarget != null)
{
@@ -127,16 +123,14 @@ public static String[] getGdbServerCommandLineArray(ILaunchConfiguration configu
lst.add("-c"); //$NON-NLS-1$
lst.add(String.format(fmtGdbPort, port));
- port = PortChecker
- .getAvailablePort(configuration.getAttribute(ConfigurationAttributes.GDB_SERVER_TELNET_PORT_NUMBER,
- DefaultPreferences.GDB_SERVER_TELNET_PORT_NUMBER_DEFAULT));
+ port = configuration.getAttribute(ConfigurationAttributes.GDB_SERVER_TELNET_PORT_NUMBER,
+ DefaultPreferences.GDB_SERVER_TELNET_PORT_NUMBER_DEFAULT);
lst.add("-c"); //$NON-NLS-1$
lst.add(String.format(fmtTelnetPort, port));
- port = PortChecker.getAvailablePort(
- Integer.parseInt(configuration.getAttribute(ConfigurationAttributes.GDB_SERVER_TCL_PORT_NUMBER,
- DefaultPreferences.GDB_SERVER_TCL_PORT_NUMBER_DEFAULT)));
+ port = Integer.parseInt(configuration.getAttribute(ConfigurationAttributes.GDB_SERVER_TCL_PORT_NUMBER,
+ DefaultPreferences.GDB_SERVER_TCL_PORT_NUMBER_DEFAULT));
lst.add("-c"); //$NON-NLS-1$
lst.add(String.format(fmtTclPort, port));
@@ -344,6 +338,27 @@ public static boolean getDoStartGdbClient(ILaunchConfiguration config) throws Co
DefaultPreferences.DO_START_GDB_CLIENT_DEFAULT);
}
+ /**
+ * Assigns free OpenOCD ports on an in-memory working copy (no {@code doSave()}).
+ * Must be called once per launch before the server command line is built.
+ */
+ public static void allocateServerPorts(ILaunchConfigurationWorkingCopy configuration) throws CoreException
+ {
+ int gdbPort = PortChecker.getAvailablePort(configuration.getAttribute(IGDBJtagConstants.ATTR_PORT_NUMBER,
+ DefaultPreferences.GDB_SERVER_GDB_PORT_NUMBER_DEFAULT));
+ configuration.setAttribute(IGDBJtagConstants.ATTR_PORT_NUMBER, gdbPort);
+
+ int telnetPort = PortChecker.getAvailablePort(configuration.getAttribute(
+ ConfigurationAttributes.GDB_SERVER_TELNET_PORT_NUMBER,
+ DefaultPreferences.GDB_SERVER_TELNET_PORT_NUMBER_DEFAULT));
+ configuration.setAttribute(ConfigurationAttributes.GDB_SERVER_TELNET_PORT_NUMBER, telnetPort);
+
+ int tclPort = PortChecker.getAvailablePort(Integer.parseInt(configuration.getAttribute(
+ ConfigurationAttributes.GDB_SERVER_TCL_PORT_NUMBER,
+ DefaultPreferences.GDB_SERVER_TCL_PORT_NUMBER_DEFAULT)));
+ configuration.setAttribute(ConfigurationAttributes.GDB_SERVER_TCL_PORT_NUMBER, String.valueOf(tclPort));
+ }
+
// ------------------------------------------------------------------------
private static boolean supportsAdapterUsbCommand(String executablePath)
diff --git a/bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/GdbBackend.java b/bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/GdbBackend.java
index c45374269..f78092ac7 100644
--- a/bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/GdbBackend.java
+++ b/bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/GdbBackend.java
@@ -44,6 +44,7 @@
import java.util.Properties;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.parser.util.StringUtil;
@@ -53,7 +54,6 @@
import org.eclipse.cdt.dsf.concurrent.ImmediateRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
import org.eclipse.cdt.dsf.concurrent.Sequence;
-import org.eclipse.cdt.dsf.concurrent.Sequence.Step;
import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants;
import org.eclipse.cdt.dsf.gdb.IGdbDebugPreferenceConstants;
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
@@ -61,11 +61,8 @@
import org.eclipse.cdt.dsf.gdb.service.IGDBBackend;
import org.eclipse.cdt.dsf.gdb.service.SessionType;
import org.eclipse.cdt.dsf.gdb.service.command.GDBControl.InitializationShutdownStep;
-import org.eclipse.cdt.dsf.gdb.service.command.GDBControl.InitializationShutdownStep.Direction;
import org.eclipse.cdt.dsf.mi.service.IMIBackend;
import org.eclipse.cdt.dsf.mi.service.IMIBackend2;
-import org.eclipse.cdt.dsf.mi.service.IMIBackend.BackendStateChangedEvent;
-import org.eclipse.cdt.dsf.mi.service.IMIBackend.State;
import org.eclipse.cdt.dsf.mi.service.command.events.MIStoppedEvent;
import org.eclipse.cdt.dsf.service.AbstractDsfService;
import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
@@ -86,6 +83,7 @@
import org.eclipse.core.variables.VariablesPlugin;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.core.ILaunchManager;
@@ -109,6 +107,7 @@
*
* This class is taken from {@link GnuMcuGdbBackend}
*/
+@SuppressWarnings("restriction")
public class GdbBackend extends AbstractDsfService implements IGDBBackend, IMIBackend2 {
private final ILaunchConfiguration fLaunchConfiguration;
@@ -570,8 +569,24 @@ public void destroy() {
}
// destroy() should be supported even if it's not spawner.
- if (getState() == State.STARTED) {
+ if (getState() == State.STARTED && fProcess != null)
+ {
fProcess.destroy();
+ if (fProcess.isAlive())
+ {
+ try
+ {
+ if (!fProcess.waitFor(2, TimeUnit.SECONDS))
+ {
+ fProcess.destroyForcibly();
+ }
+ }
+ catch (InterruptedException e)
+ {
+ Thread.currentThread().interrupt();
+ fProcess.destroyForcibly();
+ }
+ }
}
}
@@ -618,11 +633,7 @@ public void initialize(final RequestMonitor requestMonitor) {
System.out.println("GDBProcessStep.initialise()");
}
- class GDBLaunchMonitor {
- boolean fLaunched = false;
- boolean fTimedOut = false;
- }
- final GDBLaunchMonitor fGDBLaunchMonitor = new GDBLaunchMonitor();
+ final AtomicBoolean isFinished = new AtomicBoolean(false);
final RequestMonitor gdbLaunchRequestMonitor = new RequestMonitor(getExecutor(), requestMonitor) {
@Override
@@ -630,9 +641,8 @@ protected void handleCompleted() {
if (Activator.getInstance().isDebugging()) {
System.out.println("GDBProcessStep.initialise() handleCompleted()");
}
-
- if (!fGDBLaunchMonitor.fTimedOut) {
- fGDBLaunchMonitor.fLaunched = true;
+ if (isFinished.compareAndSet(false, true))
+ {
if (!isSuccess()) {
requestMonitor.setStatus(getStatus());
}
@@ -662,6 +672,11 @@ protected IStatus run(IProgressMonitor monitor) {
try {
fProcess = launchGDBProcess();
+ ILaunch launch = (ILaunch) getSession().getModelAdapter(ILaunch.class);
+ if (launch != null && fProcess != null)
+ {
+ LaunchProcessDictionary.getInstance().registerBackendProcess(launch, "gdb", fProcess); //$NON-NLS-1$
+ }
// Need to do this on the executor for thread-safety
getExecutor().submit(new DsfRunnable() {
@Override
@@ -679,8 +694,8 @@ public void run() {
return Status.OK_STATUS;
}
+ final StringBuilder errorBuilder = new StringBuilder();
BufferedReader inputReader = null;
- BufferedReader errorReader = null;
boolean success = false;
try {
// Read initial GDB prompt
@@ -696,35 +711,55 @@ public void run() {
// Failed to read initial prompt, check for error
if (!success) {
- errorReader = new BufferedReader(new InputStreamReader(getMIErrorStream()));
- String errorInfo = errorReader.readLine();
- if (errorInfo == null) {
+ Thread errorDrainThread = new Thread(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ try (BufferedReader errReader = new BufferedReader(
+ new InputStreamReader(getMIErrorStream())))
+ {
+ String errLine;
+ while ((errLine = errReader.readLine()) != null)
+ {
+ errorBuilder.append(errLine).append("\n");
+ }
+ }
+ catch (IOException e)
+ {
+ // Stream closed or error, safely ignore
+ }
+ }
+ }, "GDB Error Stream Drain");
+ errorDrainThread.setDaemon(true);
+ errorDrainThread.start();
+ errorDrainThread.join(1000);
+
+ String errorInfo = errorBuilder.toString().trim();
+
+ if (errorInfo.isEmpty())
+ {
errorInfo = "GDB prompt not read"; //$NON-NLS-1$
}
gdbLaunchRequestMonitor
.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, errorInfo, null));
}
- } catch (IOException e) {
+ }
+ catch (Exception e)
+ {
success = false;
gdbLaunchRequestMonitor.setStatus(
new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Error reading GDB output", e)); //$NON-NLS-1$
- }
-
- // In the case of failure, close the MI streams so
- // they are not leaked.
- if (!success) {
- if (inputReader != null) {
+ } finally
+ {
+ // In the case of failure, close the MI stream so it is not leaked.
+ if (!success && inputReader != null)
+ {
try {
inputReader.close();
} catch (IOException e) {
}
}
- if (errorReader != null) {
- try {
- errorReader.close();
- } catch (IOException e) {
- }
- }
}
gdbLaunchRequestMonitor.done();
@@ -736,10 +771,8 @@ public void run() {
getExecutor().schedule(new Runnable() {
@Override
public void run() {
- // Only process the event if we have not finished yet (hit
- // the breakpoint).
- if (!fGDBLaunchMonitor.fLaunched) {
- fGDBLaunchMonitor.fTimedOut = true;
+ if (isFinished.compareAndSet(false, true))
+ {
Thread jobThread = startGdbJob.getThread();
if (jobThread != null) {
jobThread.interrupt();
@@ -785,7 +818,8 @@ protected IStatus run(IProgressMonitor monitor) {
public void run() {
destroy();
- if (fMonitorJob.fMonitorExited) {
+ if (fMonitorJob != null && fMonitorJob.isMonitorExited())
+ {
// Now that we have destroyed the process,
// and that the monitoring thread was
// killed,
@@ -917,71 +951,87 @@ protected void shutdown(RequestMonitor requestMonitor) {
* the associated runtime process.
*/
private class MonitorJob extends Job {
- boolean fMonitorExited = false;
- DsfRunnable fMonitorStarted;
- Process fMonProcess;
+ private volatile boolean fMonitorExited = false;
+ private final DsfRunnable fMonitorStarted;
+ private final Process fMonProcess;
+ private final Object fLock = new Object();
+
+ MonitorJob(Process process, DsfRunnable monitorStarted)
+ {
+ super("GDB process monitor job."); //$NON-NLS-1$
+ fMonProcess = process;
+ fMonitorStarted = monitorStarted;
+ setSystem(true);
+ }
@Override
- protected IStatus run(IProgressMonitor monitor) {
- synchronized (fMonProcess) {
- getExecutor().submit(fMonitorStarted);
- try {
- fMonProcess.waitFor();
- fGDBExitValue = fMonProcess.exitValue();
+ protected IStatus run(IProgressMonitor monitor)
+ {
+ 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())
+ if (Activator.getInstance().isDebugging())
+ {
+ System.out.println("MonitorJob.run() exitValue() " + fGDBExitValue);
+ }
+
+
+ getExecutor().submit(new DsfRunnable()
+ {
+ @Override
+ public void run()
{
- // 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() ");
- }
+ if (Activator.getInstance().isDebugging())
+ {
+ System.out.println("MonitorJob.run() run() ");
+ }
- destroy();
- fBackendState = State.TERMINATED;
+ 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());
- }
- });
+ 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;
+ }
+ catch (InterruptedException ie)
+ {
+ Thread.interrupted();
+ } finally
+ {
+ synchronized (fLock)
+ {
+ 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) {
+ synchronized (fLock)
+ {
+ if (!fMonitorExited && getThread() != null)
+ {
getThread().interrupt();
}
}
}
+
+ public boolean isMonitorExited()
+ {
+ return fMonitorExited;
+ }
}
/**
diff --git a/bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/GdbConsoleCleanup.java b/bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/GdbConsoleCleanup.java
new file mode 100644
index 000000000..7749db652
--- /dev/null
+++ b/bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/GdbConsoleCleanup.java
@@ -0,0 +1,97 @@
+package com.espressif.idf.debug.gdbjtag.openocd.dsf;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.cdt.debug.ui.CDebugUIPlugin;
+import org.eclipse.cdt.debug.ui.debuggerconsole.IDebuggerConsole;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.ui.console.ConsolePlugin;
+import org.eclipse.ui.console.IConsole;
+
+import com.espressif.idf.core.logging.Logger;
+
+/**
+ * Stops CDT GDB CLI console read jobs that otherwise busy-loop on
+ * {@code LargePipedInputStream} after a stuck debug termination.
+ *
+ * Routing through {@link CDebugUIPlugin#getDebuggerConsoleManager()} handles both
+ * the basic ({@code GdbBasicCliConsole}) and full ({@code GdbFullCliConsole}) GDB
+ * consoles, since both implement {@link IDebuggerConsole}.
+ */
+public final class GdbConsoleCleanup
+{
+ private static final String GDB_CLI_JOB_MARKER = "GDB CLI"; //$NON-NLS-1$
+ private GdbConsoleCleanup()
+ {
+ }
+
+ public static void stopConsolesForLaunch(ILaunch launch)
+ {
+ if (launch == null)
+ {
+ return;
+ }
+
+ try
+ {
+ List toRemove = new ArrayList<>();
+ for (IDebuggerConsole console : CDebugUIPlugin.getDebuggerConsoleManager().getConsoles())
+ {
+ if (!launch.equals(console.getLaunch()))
+ {
+ continue;
+ }
+
+ try
+ {
+ console.stop();
+ }
+ catch (Exception e)
+ {
+ Logger.log(e);
+ }
+
+ try
+ {
+ CDebugUIPlugin.getDebuggerConsoleManager().removeConsole(console);
+ }
+ catch (Exception e)
+ {
+ Logger.log(e);
+ }
+
+ toRemove.add(console);
+ }
+
+ if (!toRemove.isEmpty())
+ {
+ ConsolePlugin.getDefault().getConsoleManager()
+ .removeConsoles(toRemove.toArray(new IConsole[0]));
+ }
+ }
+ catch (Exception e)
+ {
+ Logger.log(e);
+ }
+
+ cancelGdbCliReadJobs();
+ }
+
+ private static void cancelGdbCliReadJobs()
+ {
+ for (Job job : Job.getJobManager().find(null))
+ {
+ if (job == null)
+ {
+ continue;
+ }
+ String name = job.getName();
+ if (name != null && name.contains(GDB_CLI_JOB_MARKER))
+ {
+ job.cancel();
+ }
+ }
+ }
+}
diff --git a/bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/GdbServerBackend.java b/bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/GdbServerBackend.java
index 66ebd4eb2..1e4791f77 100644
--- a/bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/GdbServerBackend.java
+++ b/bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/GdbServerBackend.java
@@ -26,6 +26,7 @@
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.embedcdt.core.StringUtils;
import org.eclipse.embedcdt.debug.gdbjtag.core.DebugUtils;
@@ -241,7 +242,13 @@ protected Process launchGdbServerProcess(String[] commandLineArray) throws CoreE
envList.add(entry.getKey() + "=" + entry.getValue());
}
- return DebugUtils.exec(commandLineArray, envList.toArray(new String[0]), dir);
+ Process process = DebugUtils.exec(commandLineArray, envList.toArray(new String[0]), dir);
+ ILaunch launch = (ILaunch) getSession().getModelAdapter(ILaunch.class);
+ if (launch != null)
+ {
+ LaunchProcessDictionary.getInstance().registerBackendProcess(launch, "openocd", process); //$NON-NLS-1$
+ }
+ return process;
}
private boolean supportsAdapterUsbCommand(String executablePath)
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 4b2583375..800de8883 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
@@ -7,7 +7,7 @@
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
- *
+ *
* Contributors:
* Liviu Ionescu - initial version
*******************************************************************************/
@@ -17,31 +17,30 @@
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;
import java.util.concurrent.RejectedExecutionException;
import org.eclipse.cdt.debug.gdbjtag.core.IGDBJtagConstants;
-import org.eclipse.cdt.dsf.concurrent.DefaultDsfExecutor;
-import org.eclipse.cdt.dsf.concurrent.DsfRunnable;
-import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants;
import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants;
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
+import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch;
import org.eclipse.cdt.dsf.service.DsfServicesTracker;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunch;
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.embedcdt.debug.gdbjtag.core.dsf.GnuMcuLaunch;
+import com.espressif.idf.core.logging.Logger;
import com.espressif.idf.debug.gdbjtag.openocd.Activator;
import com.espressif.idf.debug.gdbjtag.openocd.Configuration;
import com.espressif.idf.debug.gdbjtag.openocd.ConfigurationAttributes;
@@ -52,162 +51,117 @@
public class Launch extends GnuMcuLaunch
{
- // ------------------------------------------------------------------------
-
ILaunchConfiguration fConfig = null;
- private DsfSession fSession;
- private DsfServicesTracker fTracker;
- private DefaultDsfExecutor fExecutor;
private IProcess openOcdServerProcess;
private IProcess gdbIProcess;
-
+
+ private boolean fDoStartGdbServer = true;
+ private boolean fDoStartGdbClient = true;
+
+ private static final int SAFETY_NET_CLEANUP_DELAY_MS = 3000;
+
private static final String SERVER_PROC_KEY = "SERVER_PROC";
private static final String GDB_PROC_KEY = "GDB_PROC";
- // ------------------------------------------------------------------------
-
public Launch(ILaunchConfiguration launchConfiguration, String mode, ISourceLocator locator)
{
-
super(launchConfiguration, mode, locator);
-
- if (Activator.getInstance().isDebugging())
- {
- System.out.println("openocd.Launch.launch(" + launchConfiguration.getName() + "," + mode + ") " + this);
- }
-
fConfig = launchConfiguration;
- fExecutor = (DefaultDsfExecutor) getDsfExecutor();
- fSession = getSession();
}
- // ------------------------------------------------------------------------
+ public void setDoStartGdbServer(boolean doStartGdbServer)
+ {
+ fDoStartGdbServer = doStartGdbServer;
+ }
- @Override
- public void initialize()
+ public boolean getDoStartGdbServer()
{
+ return fDoStartGdbServer;
+ }
- if (Activator.getInstance().isDebugging())
- {
- System.out.println("openocd.Launch.initialize() " + this);
- }
+ public void setDoStartGdbClient(boolean doStartGdbClient)
+ {
+ fDoStartGdbClient = doStartGdbClient;
+ }
- super.initialize();
+ public boolean getDoStartGdbClient()
+ {
+ return fDoStartGdbClient;
+ }
+
+ void clearProcessReferences()
+ {
+ openOcdServerProcess = null;
+ gdbIProcess = null;
+ }
- Runnable initRunnable = new DsfRunnable()
+ private void scheduleSafetyNetCleanup()
+ {
+ final ILaunch launch = this;
+ Job cleanupJob = new Job("Force debug session cleanup")
{
@Override
- public void run()
+ protected IStatus run(IProgressMonitor monitor)
{
- fTracker = new DsfServicesTracker(GdbPlugin.getBundleContext(), fSession.getId());
- // fSession.addServiceEventListener(GdbLaunch.this, null);
-
- // fInitialized = true;
- // fireChanged();
+ if (launch instanceof GdbLaunch)
+ {
+ DsfSession session = ((GdbLaunch) launch).getSession();
+ if (session != null && DsfSession.isSessionActive(session.getId()))
+ {
+ // Session is still active well after terminate(): the graceful
+ // shutdown is stuck. Re-kill the OS processes and close the MI
+ // pipes/consoles so CDT's own shutdown can finally complete.
+ LaunchProcessDictionary.getInstance().forceKillOsProcesses(launch);
+ }
+ }
+ return Status.OK_STATUS;
}
};
-
- try
- {
- cleanUpOldLaunchProcesses();
- }
- catch (CoreException e)
- {
- e.printStackTrace();
- }
-
- // Invoke the execution code and block waiting for the result.
- try
- {
- fExecutor.submit(initRunnable).get();
- }
- catch (InterruptedException e)
- {
- new Status(IStatus.ERROR, Activator.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR,
- "Error initializing launch", e); //$NON-NLS-1$
- }
- catch (ExecutionException e)
- {
- new Status(IStatus.ERROR, Activator.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR,
- "Error initializing launch", e); //$NON-NLS-1$
- }
-
+ cleanupJob.setSystem(true);
+ cleanupJob.schedule(SAFETY_NET_CLEANUP_DELAY_MS);
}
@Override
protected void provideDefaults(ILaunchConfigurationWorkingCopy config) throws CoreException
{
-
super.provideDefaults(config);
if (!config.hasAttribute(IGDBJtagConstants.ATTR_IP_ADDRESS))
- {
- config.setAttribute(IGDBJtagConstants.ATTR_IP_ADDRESS, "localhost"); //$NON-NLS-1$
- }
+ config.setAttribute(IGDBJtagConstants.ATTR_IP_ADDRESS, "localhost");
if (!config.hasAttribute(IGDBJtagConstants.ATTR_JTAG_DEVICE_ID))
- {
config.setAttribute(IGDBJtagConstants.ATTR_JTAG_DEVICE_ID, ConfigurationAttributes.JTAG_DEVICE);
- }
if (!config.hasAttribute(IGDBJtagConstants.ATTR_PORT_NUMBER))
- {
config.setAttribute(IGDBJtagConstants.ATTR_PORT_NUMBER,
DefaultPreferences.GDB_SERVER_GDB_PORT_NUMBER_DEFAULT);
- }
if (!config.hasAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUG_NAME))
- {
- DefaultPreferences fDefaultPreferences = Activator.getInstance().getDefaultPreferences();
config.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUG_NAME,
- fDefaultPreferences.getGdbClientExecutable());
- }
+ Activator.getInstance().getDefaultPreferences().getGdbClientExecutable());
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);
}
- // ------------------------------------------------------------------------
-
public void initializeServerConsole(IProgressMonitor monitor) throws CoreException
{
-
- if (Activator.getInstance().isDebugging())
- {
- System.out.println("openocd.Launch.initializeServerConsole()");
- }
-
- boolean doAddServerConsole = Configuration.getDoAddServerConsole(fConfig);
-
- if (doAddServerConsole)
+ if (Configuration.getDoAddServerConsole(fConfig))
{
-
- // Add the GDB server process to the launch tree
openOcdServerProcess = addServerProcess(Configuration.getGdbServerCommandName(fConfig));
- LaunchProcessDictionary.getInstance().addProcessToDictionary(getLaunchConfiguration().getName(), SERVER_PROC_KEY, openOcdServerProcess);
+ LaunchProcessDictionary.getInstance().addProcessToDictionary(this, SERVER_PROC_KEY, openOcdServerProcess);
monitor.worked(1);
}
}
public void initializeConsoles(IProgressMonitor monitor) throws CoreException
{
-
- if (Activator.getInstance().isDebugging())
- {
- System.out.println("openocd.Launch.initializeConsoles()");
- }
-
- {
- // Add the GDB client process to the launch tree.
- 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);
- }
+ gdbIProcess = addClientProcess(Configuration.getGdbClientCommandName(fConfig));
+ gdbIProcess.setAttribute(IProcess.ATTR_CMDLINE, Configuration.getGdbClientCommandLine(fConfig));
+ LaunchProcessDictionary.getInstance().addProcessToDictionary(this, GDB_PROC_KEY, gdbIProcess);
+ monitor.worked(1);
}
public IProcess addServerProcess(String label) throws CoreException
@@ -215,97 +169,89 @@ public IProcess addServerProcess(String label) throws CoreException
IProcess newProcess = null;
try
{
- // Add the server process object to the launch.
Process serverProc = getDsfExecutor().submit(new Callable()
{
@Override
public Process call() throws CoreException
{
- GdbServerBackend backend = fTracker.getService(GdbServerBackend.class);
- if (backend != null)
+ DsfServicesTracker tracker = new DsfServicesTracker(GdbPlugin.getBundleContext(),
+ getSession().getId());
+ try
+ {
+ GdbServerBackend backend = tracker.getService(GdbServerBackend.class);
+ return backend != null ? backend.getServerProcess() : null;
+ }
+ finally
{
- return backend.getServerProcess();
+ tracker.dispose();
}
- return null;
}
}).get();
- // Need to go through DebugPlugin.newProcess so that we can use
- // the overrideable process factory to allow others to override.
- // First set attribute to specify we want to create the gdb process.
- // Bug 210366
- Map attributes = new HashMap();
if (serverProc != null)
{
- newProcess = DebugPlugin.newProcess(this, serverProc, label, attributes);
+ newProcess = DebugPlugin.newProcess(this, serverProc, label, new HashMap());
}
}
- catch (InterruptedException e)
+ catch (Exception e)
{
- throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, 0,
- "Interrupted while waiting for get process callable.", e)); //$NON-NLS-1$
+ throw new CoreException(
+ new Status(IStatus.ERROR, Activator.PLUGIN_ID, 0, "Error adding server process.", e));
}
- catch (ExecutionException e)
- {
- throw (CoreException) e.getCause();
- }
- catch (RejectedExecutionException e)
- {
- throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, 0,
- "Debugger shut down before launch was completed.", e)); //$NON-NLS-1$
- }
-
return newProcess;
}
- // ------------------------------------------------------------------------
-
@Override
public void terminate() throws DebugException
{
- super.terminate();
-
- LaunchProcessDictionary.getInstance().killAllProcessesInLaunch(getLaunchConfiguration().getName());
+ // Start CDT's normal terminate first so the framework owns ending the DSF
+ // session and shutting down its executor. Then force-kill the OS processes and
+ // close the MI pipes/consoles: that is what unblocks the graceful shutdown when
+ // gdb/openocd are stuck (e.g. a core-reset loop), without us pre-terminating the
+ // executor and triggering RejectedExecutionException in the framework.
+ try
+ {
+ if (!isTerminated())
+ {
+ super.terminate();
+ }
+ }
+ catch (RejectedExecutionException e)
+ {
+ // DSF executor already terminating; OS processes are force-killed below.
+ }
+ catch (Exception e)
+ {
+ Logger.log(e);
+ }
+ finally
+ {
+ LaunchProcessDictionary.getInstance().forceKillOsProcesses(this);
+ clearProcessReferences();
+ scheduleSafetyNetCleanup();
+ }
}
-
+
@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();
- }
+ if (openOcdServerProcess != null)
+ processes.add(openOcdServerProcess);
+ if (gdbIProcess != null)
+ processes.add(gdbIProcess);
+ return processes.toArray(new IProcess[0]);
}
}
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 5f59eea21..1c248a1c9 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
@@ -9,8 +9,8 @@
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
- * Liviu Ionescu - initial version
- * Jonah Graham - fix for Neon
+ * Liviu Ionescu - initial version
+ * Jonah Graham - fix for Neon
*******************************************************************************/
package com.espressif.idf.debug.gdbjtag.openocd.dsf;
@@ -23,6 +23,9 @@
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor;
@@ -52,6 +55,7 @@
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.debug.core.model.ISourceLocator;
import org.eclipse.embedcdt.core.StringUtils;
@@ -59,116 +63,94 @@
import org.eclipse.embedcdt.debug.gdbjtag.core.dsf.AbstractGnuMcuLaunchConfigurationDelegate;
import org.eclipse.embedcdt.debug.gdbjtag.core.dsf.GnuMcuServerServicesLaunchSequence;
+import com.espressif.idf.core.logging.Logger;
import com.espressif.idf.debug.gdbjtag.openocd.Activator;
import com.espressif.idf.debug.gdbjtag.openocd.Configuration;
+import com.espressif.idf.debug.gdbjtag.openocd.ConfigurationAttributes;
import com.espressif.idf.debug.gdbjtag.openocd.ui.Messages;
-/**
- * This class is referred in the plugin.xml as an "org.eclipse.debug.core.launchDelegates" extension point.
- *
- * It inherits directly from the GDB Hardware Debug plug-in.
- *
- *
- */
@SuppressWarnings("restriction")
public class LaunchConfigurationDelegate extends AbstractGnuMcuLaunchConfigurationDelegate
{
+ private static final String NON_STOP_FIRST_VERSION = "6.8.50"; //$NON-NLS-1$
+ private static final int STATUS_DLL_NOT_FOUND = -1073741515;
+ private static final int SERVER_STATUS_POLL_INTERVAL_MS = 250;
+ private static final int SERVER_STATUS_POLL_TIMEOUT_MS = 30_000;
+ private static final int COMPLETE_INIT_TIMEOUT_MS = 60_000;
- // ------------------------------------------------------------------------
-
- private final static String NON_STOP_FIRST_VERSION = "6.8.50"; //$NON-NLS-1$
- private final int STATUS_DLL_NOT_FOUND = -1073741515;
+ private static final ThreadLocal pendingLaunchOptions = new ThreadLocal<>();
ILaunchConfiguration fConfig = null;
@SuppressWarnings("unused")
private boolean fIsNonStopSession = false;
- private boolean fDoStartGdbServer = false;
- private boolean fDoStartGdbClient = true;
- private boolean fIgnoreGdbClient = false;
-
- // ------------------------------------------------------------------------
@Override
protected IDsfDebugServicesFactory newServiceFactory(ILaunchConfiguration config, String version)
{
-
- if (Activator.getInstance().isDebugging())
- {
- System.out.println("openocd.LaunchConfigurationDelegate.newServiceFactory(" + config.getName() + ","
- + version + ") " + this);
- }
-
fConfig = config;
return new ServicesFactory(version, ILaunchManager.DEBUG_MODE);
- // return new GdbJtagDebugServicesFactory(version);
}
protected IDsfDebugServicesFactory newServiceFactory(ILaunchConfiguration config, String version, String mode)
{
-
- if (Activator.getInstance().isDebugging())
- {
- System.out.println("openocd.LaunchConfigurationDelegate.newServiceFactory(" + config.getName() + ","
- + version + "," + mode + ") " + this);
- }
-
fConfig = config;
return new ServicesFactory(version, mode);
- // return new GdbJtagDebugServicesFactory(version);
}
- public void ignoreGdbClient()
- {
- fIgnoreGdbClient = true;
- }
-
- public void doNotIngoreGdbClient()
+ /**
+ * Launch OpenOCD without starting the GDB client (application-level tracing).
+ */
+ public void runOpenOcdOnlyLaunch(ILaunchConfiguration config, String mode, IProgressMonitor monitor)
+ throws CoreException
{
- fIgnoreGdbClient = false;
+ pendingLaunchOptions.set(LaunchOptions.openOcdOnly());
+ try
+ {
+ config.launch(mode, monitor != null ? monitor : new NullProgressMonitor());
+ }
+ finally
+ {
+ pendingLaunchOptions.remove();
+ }
}
- /**
- * This method is called first when starting a debug session.
- */
@Override
protected GdbLaunch createGdbLaunch(ILaunchConfiguration configuration, String mode, ISourceLocator locator)
throws CoreException
{
-
- if (Activator.getInstance().isDebugging())
+ ILaunchConfigurationWorkingCopy wc = configuration.getWorkingCopy();
+ if (Configuration.getDoStartGdbServer(wc))
{
- System.out.println("openocd.LaunchConfigurationDelegate.createGdbLaunch(" + configuration.getName() + ","
- + mode + ") " + this);
+ Configuration.allocateServerPorts(wc);
}
- fDoStartGdbServer = Configuration.getDoStartGdbServer(configuration);
- if (!fIgnoreGdbClient)
+ Launch launch = new Launch(wc, mode, locator);
+ launch.setDoStartGdbServer(Configuration.getDoStartGdbServer(wc));
+
+ LaunchOptions options = pendingLaunchOptions.get();
+ if (options != null && options.isOpenOcdOnly())
{
- fDoStartGdbClient = Configuration.getDoStartGdbClient(configuration);
+ wc.setAttribute(ConfigurationAttributes.DO_START_GDB_CLIENT, false);
+ launch.setDoStartGdbClient(false);
+ }
+ else
+ {
+ launch.setDoStartGdbClient(Configuration.getDoStartGdbClient(wc));
}
- DebugUtils.checkLaunchConfigurationStarted(configuration);
-
- // return new GdbLaunch(configuration, mode, locator);
- return new Launch(configuration, mode, locator);
+ DebugUtils.checkLaunchConfigurationStarted(wc);
+ return launch;
}
@Override
protected String getGDBVersion(ILaunchConfiguration config) throws CoreException
{
-
String gdbClientCommand = Configuration.getGdbClientCommand(config, null);
- String version = getGDBVersion(config, gdbClientCommand);
- if (Activator.getInstance().isDebugging())
- {
- System.out.println("openocd.LaunchConfigurationDelegate.getGDBVersion " + version);
- }
- return version;
+ return getGDBVersion(config, gdbClientCommand);
}
private String getGDBVersion(final ILaunchConfiguration configuration, String gdbClientCommand) throws CoreException
{
-
String[] cmdArray = new String[2];
cmdArray[0] = gdbClientCommand;
cmdArray[1] = "--version";
@@ -181,23 +163,17 @@ private String getGDBVersion(final ILaunchConfiguration configuration, String gd
catch (IOException e)
{
throw new DebugException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, DebugException.REQUEST_FAILED,
- "Error while launching command: " + StringUtils.join(cmdArray, " "), e.getCause()));//$NON-NLS-2$
+ "Error while launching command: " + StringUtils.join(cmdArray, " "), e.getCause()));
}
- // Start a timeout job to make sure we don't get stuck waiting for
- // an answer from a gdb that is hanging
- // Bug 376203
- Job timeoutJob = new Job("GDB version timeout job") //$NON-NLS-1$
+ Job timeoutJob = new Job("GDB version timeout job")
{
{
setSystem(true);
}
-
@Override
protected IStatus run(IProgressMonitor arg)
{
- // Took too long. Kill the gdb process and
- // let things clean up.
process.destroy();
return Status.OK_STATUS;
}
@@ -215,27 +191,16 @@ protected IStatus run(IProgressMonitor arg)
String line;
while ((line = reader.readLine()) != null)
{
- cmdOutput.append(line);
- cmdOutput.append('\n'); // $NON-NLS-1$
+ cmdOutput.append(line).append('\n');
}
}
catch (IOException e)
{
throw new DebugException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, DebugException.REQUEST_FAILED,
- "Error reading GDB STDOUT after sending: " + StringUtils.join(cmdArray, " ") + ", response: "
- + cmdOutput,
- e.getCause()));// $NON-NLS-1$
+ "Error reading GDB STDOUT", e.getCause()));
} finally
{
- // If we get here we are obviously not stuck so we can cancel the
- // timeout job.
- // Note that it may already have executed, but that is not a
- // problem.
timeoutJob.cancel();
-
- // Cleanup to avoid leaking pipes
- // Close the stream we used, and then destroy the process
- // Bug 345164
if (stream != null)
{
try
@@ -253,68 +218,80 @@ protected IStatus run(IProgressMonitor arg)
if (gdbVersion == null || gdbVersion.isEmpty())
{
String errorMessage = process.exitValue() == STATUS_DLL_NOT_FOUND ? Messages.DllNotFound_ExceptionMessage
- : cmdOutput.toString();
+ : cmdOutput.toString().trim();
+ if (errorMessage.isEmpty())
+ {
+ errorMessage = "Could not determine GDB version";
+ }
throw new DebugException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, DebugException.REQUEST_FAILED,
- "Could not determine GDB version after sending: " + StringUtils.join(cmdArray, " ")
- + ", response: \n" + errorMessage + "\nERROR CODE:" + process.exitValue(),
- null));// $NON-NLS-1$ // $NON-NLS-2$
+ errorMessage, null));
}
-
return gdbVersion;
}
- public void launchWithoutGdbClient(ILaunchConfiguration config, String mode, ILaunch launch,
- IProgressMonitor monitor) throws CoreException
- {
- fDoStartGdbClient = false;
- launch(config, mode, launch, monitor);
- }
-
- /**
- * After Launch.initialise(), call here to effectively launch.
- *
- * The main reason for this is the custom launchDebugSession().
- */
@Override
public void launch(ILaunchConfiguration config, String mode, ILaunch launch, IProgressMonitor monitor)
throws CoreException
{
+ org.eclipse.cdt.launch.LaunchUtils.enableActivity("org.eclipse.cdt.debug.dsfgdbActivity", true);
- if (Activator.getInstance().isDebugging())
- {
- System.out.println(
- "openocd.LaunchConfigurationDelegate.launch(" + config.getName() + "," + mode + ") " + this);
- }
+ final IProgressMonitor finalMonitor = monitor == null ? new NullProgressMonitor() : monitor;
+ final Thread mainLaunchThread = Thread.currentThread();
- org.eclipse.cdt.launch.LaunchUtils.enableActivity("org.eclipse.cdt.debug.dsfgdbActivity", true); //$NON-NLS-1$
- if (monitor == null)
- {
- monitor = new NullProgressMonitor();
- }
+ Thread cancelWatcher = new Thread(() -> {
+ try
+ {
+ while (!finalMonitor.isCanceled() && !launch.isTerminated())
+ {
+ Thread.sleep(200);
+ }
+
+ if (finalMonitor.isCanceled())
+ {
+ if (!launch.isTerminated() && launch.canTerminate())
+ {
+ try
+ {
+ launch.terminate();
+ }
+ catch (Exception e)
+ {
+ Logger.log(e);
+ }
+ }
+ mainLaunchThread.interrupt();
+ }
+ }
+ catch (InterruptedException e)
+ {
+ Thread.currentThread().interrupt();
+ }
+ });
+
+ cancelWatcher.setDaemon(true);
+ cancelWatcher.setName("Launch Cancel Watcher");
+ cancelWatcher.start();
- if (mode.equals(ILaunchManager.DEBUG_MODE) || mode.equals(ILaunchManager.RUN_MODE))
+ try
+ {
+ if (mode.equals(ILaunchManager.DEBUG_MODE) || mode.equals(ILaunchManager.RUN_MODE))
+ {
+ launchDebugger(config, launch, finalMonitor);
+ }
+ } finally
{
- launchDebugger(config, launch, monitor);
+ cancelWatcher.interrupt();
+ Thread.interrupted();
}
}
private void launchDebugger(ILaunchConfiguration config, ILaunch launch, IProgressMonitor monitor)
throws CoreException
{
+ Launch idfLaunch = (Launch) launch;
+ int totalWork = idfLaunch.getDoStartGdbServer() ? 11 : 10;
+ monitor.beginTask(LaunchMessages.getString("GdbLaunchDelegate.0"), totalWork);
- if (Activator.getInstance().isDebugging())
- {
- System.out.println("openocd.LaunchConfigurationDelegate.launchDebugger(" + config.getName() + ") " + this);
- }
-
- int totalWork = 10;
- if (fDoStartGdbServer)
- {
- // Extra units due to server console
- totalWork += 1;
- }
-
- monitor.beginTask(LaunchMessages.getString("GdbLaunchDelegate.0"), totalWork); //$NON-NLS-1$
if (monitor.isCanceled())
{
cleanupLaunch(launch);
@@ -330,23 +307,10 @@ private void launchDebugger(ILaunchConfiguration config, ILaunch launch, IProgre
}
}
- /** @since 4.1 */
@Override
protected void launchDebugSession(final ILaunchConfiguration config, ILaunch l, IProgressMonitor monitor)
throws CoreException
{
-
- if (Activator.getInstance().isDebugging())
- {
- System.out.println(
- "openocd.LaunchConfigurationDelegate.launchDebugSession(" + config.getName() + ") " + this);
- }
-
- // From here it is almost identical with the system one, except
- // the console creation, explicitly marked with '+++++'.
-
- // --------------------------------------------------------------------
-
if (monitor.isCanceled())
{
cleanupLaunch(l);
@@ -355,135 +319,90 @@ protected void launchDebugSession(final ILaunchConfiguration config, ILaunch l,
SessionType sessionType = LaunchUtils.getSessionType(config);
boolean attach = LaunchUtils.getIsAttach(config);
-
- final GdbLaunch launch = (GdbLaunch) l;
+ final Launch launch = (Launch) l;
if (sessionType == SessionType.REMOTE)
- {
- monitor.subTask(LaunchMessages.getString("GdbLaunchDelegate.1")); //$NON-NLS-1$
- }
+ monitor.subTask(LaunchMessages.getString("GdbLaunchDelegate.1"));
else if (sessionType == SessionType.CORE)
- {
- monitor.subTask(LaunchMessages.getString("GdbLaunchDelegate.2")); //$NON-NLS-1$
- }
+ monitor.subTask(LaunchMessages.getString("GdbLaunchDelegate.2"));
else
- {
- assert sessionType == SessionType.LOCAL : "Unexpected session type: " + sessionType.toString(); //$NON-NLS-1$
- monitor.subTask(LaunchMessages.getString("GdbLaunchDelegate.3")); //$NON-NLS-1$
- }
+ monitor.subTask(LaunchMessages.getString("GdbLaunchDelegate.3"));
- // An attach session does not need to necessarily have an
- // executable specified. This is because:
- // - In remote multi-process attach, there will be more than one
- // executable
- // In this case executables need to be specified differently.
- // The current solution is to use the solib-search-path to specify
- // the path of any executable we can attach to.
- // - In local single process, GDB has the ability to find the executable
- // automatically.
if (!attach)
- {
checkBinaryDetails(config);
- }
monitor.worked(1);
-
- // Must set this here for users that call directly the deprecated
- // newServiceFactory(String)
fIsNonStopSession = LaunchUtils.getIsNonStopMode(config);
- String gdbVersion = getGDBVersion(config);
-
- // First make sure non-stop is supported, if the user want to use this
- // mode
- if (LaunchUtils.getIsNonStopMode(config) && !isNonStopSupportedInGdbVersion(gdbVersion))
+ if (launch.getDoStartGdbClient())
{
- cleanupLaunch(launch);
- throw new DebugException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, DebugException.REQUEST_FAILED,
- "Non-stop mode is not supported for GDB " + gdbVersion + ", GDB " + NON_STOP_FIRST_VERSION //$NON-NLS-1$ //$NON-NLS-2$
- + " or higher is required.", //$NON-NLS-1$
- null));
- }
+ String gdbVersion = getGDBVersion(config);
+
+ if (LaunchUtils.getIsNonStopMode(config) && !isNonStopSupportedInGdbVersion(gdbVersion))
+ {
+ cleanupLaunch(launch);
+ throw new DebugException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, DebugException.REQUEST_FAILED,
+ "Non-stop mode is not supported", null));
+ }
+
+ if (LaunchUtils.getIsPostMortemTracing(config) && !isPostMortemTracingSupportedInGdbVersion(gdbVersion))
+ {
+ cleanupLaunch(launch);
+ throw new DebugException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, DebugException.REQUEST_FAILED,
+ "Post-mortem tracing is not supported", null));
+ }
- if (LaunchUtils.getIsPostMortemTracing(config) && !isPostMortemTracingSupportedInGdbVersion(gdbVersion))
+ launch.setServiceFactory(newServiceFactory(config, gdbVersion, launch.getLaunchMode()));
+ }
+ else
{
- cleanupLaunch(launch);
- throw new DebugException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, DebugException.REQUEST_FAILED,
- "Post-mortem tracing is not supported for GDB " + gdbVersion + ", GDB " + NON_STOP_FIRST_VERSION //$NON-NLS-1$ //$NON-NLS-2$
- + " or higher is required.", //$NON-NLS-1$
- null));
+ launch.setServiceFactory(newServiceFactory(config, "7.0", launch.getLaunchMode()));
}
- launch.setServiceFactory(newServiceFactory(config, gdbVersion, launch.getLaunchMode()));
-
- // Time to start the DSF stuff. First initialize the launch.
- // We do this here to avoid having to cleanup in case
- // the launch is cancelled above.
- // This initialize() call is the first thing that requires cleanup
- // followed by the steps further down which also need cleanup.
launch.initialize();
- // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
-
boolean succeed = false;
-
- // Assign 4 work ticks.
IProgressMonitor subMonServer = new SubProgressMonitor(monitor, 4,
SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK);
-
Sequence serverServicesLaunchSequence = getServerServicesSequence(launch.getSession(), launch, subMonServer);
try
{
- // Execute on DSF thread and wait for it.
launch.getSession().getExecutor().execute(serverServicesLaunchSequence);
serverServicesLaunchSequence.get();
succeed = true;
}
catch (InterruptedException e1)
{
- throw new DebugException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, DebugException.INTERNAL_ERROR,
- "Interrupted Exception in dispatch thread", e1)); //$NON-NLS-1$
+ if (monitor.isCanceled())
+ return;
+ throw new DebugException(
+ new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, DebugException.INTERNAL_ERROR, "Interrupted", e1));
}
catch (ExecutionException e1)
{
- if (e1.getMessage().contains("Starting OpenOCD timed out.")) //$NON-NLS-1$
+ if (e1.getMessage() != null && e1.getMessage().contains("Starting OpenOCD timed out."))
{
- IStatus status = new Status(IStatus.OK, Activator.PLUGIN_ID, DebugException.REQUEST_FAILED,
- "Error in services launch sequence", e1.getCause()); //$NON-NLS-1$
+ IStatus status = new Status(IStatus.ERROR, Activator.PLUGIN_ID,
+ Activator.OPENOCD_STARTUP_TIMEOUT_STATUS, "Timeout", e1.getCause());
DebugPlugin.getDefault().getStatusHandler(status).handleStatus(status, null);
throw new DebugException(Status.OK_STATUS);
}
- else
- {
- throw new DebugException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, DebugException.REQUEST_FAILED,
- "Error in services launch sequence", e1.getCause())); //$NON-NLS-1$
- }
-
+ throw new DebugException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, DebugException.REQUEST_FAILED,
+ "Error", e1.getCause()));
}
catch (CancellationException e1)
{
- // Launch aborted, so exit cleanly
- if (Activator.getInstance().isDebugging())
- {
- System.out.println("openocd.LaunchConfigurationDelegate.launchDebugger() aborted, so exit cleanly");
- }
return;
} finally
{
if (!succeed)
- {
cleanupLaunch(launch);
- }
}
- if (fDoStartGdbServer)
+ if (launch.getDoStartGdbServer())
{
-
- // This contributes 1 work units to the monitor
- ((Launch) launch).initializeServerConsole(monitor);
-
- // Wait for the server to be available, or to know it failed.
+ launch.initializeServerConsole(monitor);
IStatus serverStatus;
try
{
@@ -494,40 +413,38 @@ public IStatus call() throws CoreException
{
DsfServicesTracker tracker = new DsfServicesTracker(GdbPlugin.getBundleContext(),
launch.getSession().getId());
- GdbServerBackend backend = tracker.getService(GdbServerBackend.class);
- if (backend != null)
- {
- return backend.getServerExitStatus();
- }
- else
+ try
{
+ GdbServerBackend backend = tracker.getService(GdbServerBackend.class);
+ if (backend != null)
+ return backend.getServerExitStatus();
throw new CoreException(
new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Could not start GDB server."));
}
+ finally
+ {
+ tracker.dispose();
+ }
}
};
- // Wait to get the server status. Being endless should not be a
- // problem, the timeout will kill it if too long.
+ long deadline = System.currentTimeMillis() + SERVER_STATUS_POLL_TIMEOUT_MS;
serverStatus = null;
while (serverStatus == null)
{
if (monitor.isCanceled())
{
- if (Activator.getInstance().isDebugging())
- {
- System.out.println(
- "openocd.LaunchConfigurationDelegate.launchDebugSession() sleep cancelled" + this);
- }
cleanupLaunch(launch);
return;
}
- Thread.sleep(10);
- serverStatus = launch.getSession().getExecutor().submit(callable).get();
- if (Activator.getInstance().isDebugging())
+ if (System.currentTimeMillis() >= deadline)
{
- System.out.print('!');
+ cleanupLaunch(launch);
+ throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID,
+ Activator.OPENOCD_STARTUP_TIMEOUT_STATUS, "OpenOCD server status timed out.", null));
}
+ Thread.sleep(SERVER_STATUS_POLL_INTERVAL_MS);
+ serverStatus = launch.getSession().getExecutor().submit(callable).get();
}
if (serverStatus != Status.OK_STATUS)
@@ -537,49 +454,32 @@ public IStatus call() throws CoreException
cleanupLaunch(launch);
return;
}
- if (Activator.getInstance().isDebugging())
- {
- System.out.println("openocd.LaunchConfigurationDelegate.launchDebugger() " + serverStatus);
- }
throw new CoreException(serverStatus);
}
-
}
catch (InterruptedException e)
{
+ if (monitor.isCanceled())
+ {
+ cleanupLaunch(launch);
+ return;
+ }
Activator.log(e);
}
catch (ExecutionException e)
{
Activator.log(e);
}
-
- if (Activator.getInstance().isDebugging())
- {
- System.out.println(
- "openocd.LaunchConfigurationDelegate.launchDebugSession() * Server start confirmed. *");
- }
}
- if (!fDoStartGdbClient)
- {
- if (Activator.getInstance().isDebugging())
- {
- System.out.println(
- "openocd.LaunchConfigurationDelegate.launchDebugSession() No GDB client, abruptly return.");
- }
+ if (!launch.getDoStartGdbClient())
return;
- }
-
- // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- // Create and invoke the launch sequence to create the debug control and
- // services
IProgressMonitor subMon1 = new SubProgressMonitor(monitor, 4, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK);
Sequence servicesLaunchSequence = getServicesSequence(launch.getSession(), launch, subMon1);
launch.getSession().getExecutor().execute(servicesLaunchSequence);
- // boolean succeed = false;
+ succeed = false;
try
{
servicesLaunchSequence.get();
@@ -587,24 +487,23 @@ public IStatus call() throws CoreException
}
catch (InterruptedException e1)
{
- throw new DebugException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, DebugException.INTERNAL_ERROR,
- "Interrupted Exception in dispatch thread", e1)); //$NON-NLS-1$
+ if (monitor.isCanceled())
+ return;
+ throw new DebugException(
+ new Status(IStatus.ERROR, Activator.PLUGIN_ID, DebugException.INTERNAL_ERROR, "Interrupted", e1));
}
catch (ExecutionException e1)
{
throw new DebugException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, DebugException.REQUEST_FAILED,
- "Error in services launch sequence", e1.getCause())); //$NON-NLS-1$
+ "Error", e1.getCause()));
}
catch (CancellationException e1)
{
- // Launch aborted, so exit cleanly
return;
} finally
{
if (!succeed)
- {
cleanupLaunch(launch);
- }
}
if (monitor.isCanceled())
@@ -613,30 +512,11 @@ public IStatus call() throws CoreException
return;
}
- // The initializeControl method should be called after the
- // ICommandControlService
- // is initialised in the ServicesLaunchSequence above. This is because
- // it is that
- // service that will trigger the launch cleanup (if we need it during
- // this launch)
- // through an ICommandControlShutdownDMEvent
launch.initializeControl();
+ launch.initializeConsoles(monitor);
- // Add the GDB process object to the launch.
-
- // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
- // launch.addCLIProcess("gdb"); //$NON-NLS-1$
- // monitor.worked(1);
-
- // This contributes one work units for the GDB client console
- // and optionally one for the semihosting console.
- ((Launch) launch).initializeConsoles(monitor);
- // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
- // Create and invoke the final launch sequence to setup GDB
final IProgressMonitor subMon2 = new SubProgressMonitor(monitor, 4,
SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK);
-
Query