Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarc-Andre Laperle2016-08-28 05:13:36 +0000
committerMarc-Andre Laperle2016-08-28 15:18:19 +0000
commitb71a94f4f2874f39011ccafe0b3a7197b4af07fe (patch)
treefb6ff735b39fc176fac5cc4d9a378984b49f6787
parent228ccf98e3151c59c2d651d966bcc9d777662957 (diff)
downloadorg.eclipse.cdt-b71a94f4f2874f39011ccafe0b3a7197b4af07fe.tar.gz
org.eclipse.cdt-b71a94f4f2874f39011ccafe0b3a7197b4af07fe.tar.xz
org.eclipse.cdt-b71a94f4f2874f39011ccafe0b3a7197b4af07fe.zip
LLDB: Print an error when lldb is below minimum version
Prevent users from using a version of lldb that is too old. Before version 3.8 there was a hang and other issues. If the version cannot be determined, the launch stil proceeds. This is because there is no good way to get the version from the lldb-mi executable but rather, we can only do a best-effort to get the version from the normal lldb executable. If the lldb executable is not present but the lldb-mi is, this is a valid way to use the debugger but the version cannot be determined. In the future, it would be better if the lldb-mi executable could report the lldb version directly. But this would be an improvement in lldb, not CDT. Change-Id: Ief8a4ebd3ea1e3d549a5cef41ac3030ec48734c4 Signed-off-by: Marc-Andre Laperle <marc-andre.laperle@ericsson.com> (cherry picked from commit e5d8f677b2ae30a6010a9415e5596eaac752bb97)
-rw-r--r--llvm/org.eclipse.cdt.llvm.dsf.lldb.core/src/org/eclipse/cdt/llvm/dsf/lldb/core/ILLDBLaunchConfigurationConstants.java3
-rw-r--r--llvm/org.eclipse.cdt.llvm.dsf.lldb.core/src/org/eclipse/cdt/llvm/dsf/lldb/core/internal/ILLDBConstants.java27
-rw-r--r--llvm/org.eclipse.cdt.llvm.dsf.lldb.core/src/org/eclipse/cdt/llvm/dsf/lldb/core/internal/LLDBCorePlugin.java80
-rw-r--r--llvm/org.eclipse.cdt.llvm.dsf.lldb.core/src/org/eclipse/cdt/llvm/dsf/lldb/core/internal/launching/LLDBLaunch.java265
-rw-r--r--llvm/org.eclipse.cdt.llvm.dsf.lldb.core/src/org/eclipse/cdt/llvm/dsf/lldb/core/internal/launching/Messages.java1
-rw-r--r--llvm/org.eclipse.cdt.llvm.dsf.lldb.core/src/org/eclipse/cdt/llvm/dsf/lldb/core/internal/launching/messages.properties1
6 files changed, 376 insertions, 1 deletions
diff --git a/llvm/org.eclipse.cdt.llvm.dsf.lldb.core/src/org/eclipse/cdt/llvm/dsf/lldb/core/ILLDBLaunchConfigurationConstants.java b/llvm/org.eclipse.cdt.llvm.dsf.lldb.core/src/org/eclipse/cdt/llvm/dsf/lldb/core/ILLDBLaunchConfigurationConstants.java
index 6a778e45683..c1c4e6085d3 100644
--- a/llvm/org.eclipse.cdt.llvm.dsf.lldb.core/src/org/eclipse/cdt/llvm/dsf/lldb/core/ILLDBLaunchConfigurationConstants.java
+++ b/llvm/org.eclipse.cdt.llvm.dsf.lldb.core/src/org/eclipse/cdt/llvm/dsf/lldb/core/ILLDBLaunchConfigurationConstants.java
@@ -8,6 +8,7 @@
package org.eclipse.cdt.llvm.dsf.lldb.core;
+import org.eclipse.cdt.llvm.dsf.lldb.core.internal.ILLDBConstants;
import org.eclipse.cdt.llvm.dsf.lldb.core.internal.LLDBCorePlugin;
/**
@@ -27,5 +28,5 @@ public class ILLDBLaunchConfigurationConstants {
/**
* Launch configuration attribute value. The key is ATTR_DEBUG_NAME.
*/
- public static final String DEBUGGER_DEBUG_NAME_DEFAULT = "lldb-mi"; //$NON-NLS-1$
+ public static final String DEBUGGER_DEBUG_NAME_DEFAULT = ILLDBConstants.LLDB_MI_EXECUTABLE_NAME;
}
diff --git a/llvm/org.eclipse.cdt.llvm.dsf.lldb.core/src/org/eclipse/cdt/llvm/dsf/lldb/core/internal/ILLDBConstants.java b/llvm/org.eclipse.cdt.llvm.dsf.lldb.core/src/org/eclipse/cdt/llvm/dsf/lldb/core/internal/ILLDBConstants.java
new file mode 100644
index 00000000000..d39a43ef887
--- /dev/null
+++ b/llvm/org.eclipse.cdt.llvm.dsf.lldb.core/src/org/eclipse/cdt/llvm/dsf/lldb/core/internal/ILLDBConstants.java
@@ -0,0 +1,27 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Ericsson.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.cdt.llvm.dsf.lldb.core.internal;
+
+/**
+ * Constants related to the LLDB debugger itself.
+ *
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface ILLDBConstants {
+
+ /**
+ * The executable name for lldb-mi
+ */
+ public static final String LLDB_MI_EXECUTABLE_NAME = "lldb-mi"; //$NON-NLS-1$
+
+ /**
+ * The executable name for lldb
+ */
+ public static final String LLDB_EXECUTABLE_NAME = "lldb"; //$NON-NLS-1$
+}
diff --git a/llvm/org.eclipse.cdt.llvm.dsf.lldb.core/src/org/eclipse/cdt/llvm/dsf/lldb/core/internal/LLDBCorePlugin.java b/llvm/org.eclipse.cdt.llvm.dsf.lldb.core/src/org/eclipse/cdt/llvm/dsf/lldb/core/internal/LLDBCorePlugin.java
index 9dbcc9cb190..9713792f259 100644
--- a/llvm/org.eclipse.cdt.llvm.dsf.lldb.core/src/org/eclipse/cdt/llvm/dsf/lldb/core/internal/LLDBCorePlugin.java
+++ b/llvm/org.eclipse.cdt.llvm.dsf.lldb.core/src/org/eclipse/cdt/llvm/dsf/lldb/core/internal/LLDBCorePlugin.java
@@ -13,7 +13,11 @@
*******************************************************************************/
package org.eclipse.cdt.llvm.dsf.lldb.core.internal;
+import org.eclipse.cdt.core.model.CModelException;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Plugin;
+import org.eclipse.core.runtime.Status;
import org.osgi.framework.BundleContext;
/**
@@ -47,4 +51,80 @@ public class LLDBCorePlugin extends Plugin {
public static LLDBCorePlugin getDefault() {
return plugin;
}
+
+ /**
+ * Creates an IStatus for this plug-in using a message.
+ *
+ * @param msg
+ * the message
+ * @return an IStatus
+ * @noreference This method is not intended to be referenced by clients.
+ */
+ public static IStatus createStatus(String msg) {
+ return createStatus(msg, null);
+ }
+
+ /**
+ * Creates an IStatus for this plug-in using a message and exception.
+ *
+ * @param msg
+ * the message
+ * @param e
+ * the exception
+ * @return an IStatus
+ * @noreference This method is not intended to be referenced by clients.
+ */
+ public static IStatus createStatus(String msg, Throwable e) {
+ return new Status(IStatus.ERROR, PLUGIN_ID, msg, e);
+ }
+
+ /**
+ * Logs an IStatus
+ *
+ * @param status the IStatus
+ *
+ * @noreference This method is not intended to be referenced by clients.
+ */
+ public static void log(IStatus status) {
+ getDefault().getLog().log(status);
+ }
+
+ /**
+ * Logs a messages with exception.
+ *
+ * @param message
+ * the message
+ * @param e
+ * the exception
+ *
+ * @noreference This method is not intended to be referenced by clients.
+ */
+ public static void log(String message, Throwable e) {
+ Throwable nestedException;
+ if (e instanceof CModelException && (nestedException = ((CModelException) e).getException()) != null) {
+ e = nestedException;
+ }
+ log(createStatus(message, e));
+ }
+
+ /**
+ * Logs an exception.
+ *
+ * @param e
+ * the exception
+ *
+ * @noreference This method is not intended to be referenced by clients.
+ */
+ public static void log(Throwable e) {
+ if (e instanceof CoreException) {
+ log(((CoreException) e).getStatus());
+ } else {
+ String msg = e.getMessage();
+ if (msg == null) {
+ log("Error", e); //$NON-NLS-1$
+ } else {
+ log("Error: " + msg, e); //$NON-NLS-1$
+ }
+ }
+ }
}
diff --git a/llvm/org.eclipse.cdt.llvm.dsf.lldb.core/src/org/eclipse/cdt/llvm/dsf/lldb/core/internal/launching/LLDBLaunch.java b/llvm/org.eclipse.cdt.llvm.dsf.lldb.core/src/org/eclipse/cdt/llvm/dsf/lldb/core/internal/launching/LLDBLaunch.java
index 06e76cdabc4..a5ec7f02c32 100644
--- a/llvm/org.eclipse.cdt.llvm.dsf.lldb.core/src/org/eclipse/cdt/llvm/dsf/lldb/core/internal/launching/LLDBLaunch.java
+++ b/llvm/org.eclipse.cdt.llvm.dsf.lldb.core/src/org/eclipse/cdt/llvm/dsf/lldb/core/internal/launching/LLDBLaunch.java
@@ -8,15 +8,33 @@
package org.eclipse.cdt.llvm.dsf.lldb.core.internal.launching;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.text.MessageFormat;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.cdt.core.parser.util.StringUtil;
import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch;
import org.eclipse.cdt.llvm.dsf.lldb.core.ILLDBDebugPreferenceConstants;
import org.eclipse.cdt.llvm.dsf.lldb.core.ILLDBLaunchConfigurationConstants;
+import org.eclipse.cdt.llvm.dsf.lldb.core.internal.ILLDBConstants;
import org.eclipse.cdt.llvm.dsf.lldb.core.internal.LLDBCorePlugin;
+import org.eclipse.cdt.utils.CommandLineUtil;
+import org.eclipse.cdt.utils.spawner.ProcessFactory;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.variables.VariablesPlugin;
+import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.model.ISourceLocator;
@@ -25,6 +43,15 @@ import org.eclipse.debug.core.model.ISourceLocator;
*/
public class LLDBLaunch extends GdbLaunch {
+ private static final String XCODE_HINT = "(Xcode 7.3.1)"; //$NON-NLS-1$
+ private static final IntegerTuple LLDB_MINIMUM_REVISION = new IntegerTuple(350, 0, 21, 9);
+ private static final IntegerTuple LLDB_MINIMUM_VERSION = new IntegerTuple(3, 8, 0);
+ private static final Pattern LLDB_VERSION_PATTERN = Pattern.compile("lldb\\s*version\\s*(\\d+)\\.(\\d+)\\.(\\d+).*"); //$NON-NLS-1$ ;
+ private static final Pattern LLDB_REVISION_PATTERN = Pattern.compile("lldb-(\\d+)\\.(\\d+)\\.(\\d+)(\\.(\\d)+)?"); //$NON-NLS-1$
+
+ private IntegerTuple fLldbVersion;
+ private IntegerTuple fLldbRevision;
+
/**
* Constructs a launch.
*
@@ -80,6 +107,244 @@ public class LLDBLaunch extends GdbLaunch {
return getDefaultLLDBPath();
}
+ @Override
+ public String getGDBVersion() throws CoreException {
+ String gdbVersion = super.getGDBVersion();
+ computeLLDBVersions();
+ if (fLldbRevision != null) {
+ if (fLldbRevision.compareTo(LLDB_MINIMUM_REVISION) < 0) {
+ throw new DebugException(LLDBCorePlugin.createStatus(MessageFormat.format(
+ Messages.LLDBLaunch_minimum_version_error, fLldbRevision, LLDB_MINIMUM_REVISION, XCODE_HINT)));
+ }
+ } else if (fLldbVersion != null) {
+ if (fLldbVersion.compareTo(LLDB_MINIMUM_VERSION) < 0) {
+ throw new DebugException(LLDBCorePlugin.createStatus(MessageFormat
+ .format(Messages.LLDBLaunch_minimum_version_error, fLldbVersion, LLDB_MINIMUM_VERSION, ""))); //$NON-NLS-1$
+ }
+ }
+
+ return gdbVersion;
+ }
+
+ private void computeLLDBVersions() throws CoreException {
+ if (fLldbRevision != null || fLldbVersion != null) {
+ return;
+ }
+
+ // LLDB-MI always outputs the GDB version so try LLDB (non-MI)
+ // FIXME: There should be a better way to get the lldb version number
+ // from lldb-mi
+ IPath lldbMiPath = getGDBPath();
+ String lastSegment = lldbMiPath.lastSegment();
+ if (lastSegment.contains(ILLDBConstants.LLDB_MI_EXECUTABLE_NAME)) {
+ lastSegment = lastSegment.replace(ILLDBConstants.LLDB_MI_EXECUTABLE_NAME, ILLDBConstants.LLDB_EXECUTABLE_NAME);
+ }
+ lldbMiPath = lldbMiPath.removeLastSegments(1).append(lastSegment);
+
+ String cmd = lldbMiPath + " --version"; //$NON-NLS-1$
+
+ // Parse cmd to properly handle spaces and such things (bug 458499)
+ String[] args = CommandLineUtil.argumentsToArray(cmd);
+
+ Process process = null;
+ Job timeoutJob = null;
+ try {
+ process = ProcessFactory.getFactory().exec(args, getLaunchEnvironment());
+
+ // Start a timeout job to make sure we don't get stuck waiting for
+ // an answer from a gdb that is hanging
+ // Bug 376203
+ final Process finalProc = process;
+ timeoutJob = new Job("LLDB version timeout job") { //$NON-NLS-1$
+ {
+ setSystem(true);
+ }
+
+ @Override
+ protected IStatus run(IProgressMonitor arg) {
+ // Took too long. Kill the lldb process and
+ // let things clean up.
+ finalProc.destroy();
+ return Status.OK_STATUS;
+ }
+ };
+ timeoutJob.schedule(10000);
+
+ String streamOutput = readStream(process.getInputStream());
+
+ fLldbVersion = getLLDBVersionFromText(streamOutput);
+ fLldbRevision = getLLDBRevisionFromText(streamOutput);
+ if (fLldbVersion == null && fLldbRevision == null) {
+ if (!streamOutput.isEmpty()) {
+ // We got some output but couldn't parse it. Make that
+ // output visible to the user in the error dialog.
+ Exception detailedException = new Exception("Unexpected output format: \n\n" + streamOutput); //$NON-NLS-1$
+ throw new DebugException(LLDBCorePlugin.createStatus("Could not determine LLDB version using command: " + StringUtil.join(args, " "), //$NON-NLS-1$ //$NON-NLS-2$
+ detailedException));
+ }
+ }
+ } catch (IOException e) {
+ // Since we can't use lldb-mi for version checking, we try to use
+ // the lldb executable but it's possible that it's not there at all
+ // and that shouldn't prevent users to start debugging with lldb-mi.
+ // So here we log instead of throwing an exception and stopping the
+ // launch.
+ LLDBCorePlugin.log(new DebugException(new Status(IStatus.ERROR, LLDBCorePlugin.PLUGIN_ID, DebugException.REQUEST_FAILED,
+ "Error with command: " + StringUtil.join(args, " "), e))); //$NON-NLS-1$ //$NON-NLS-2$
+ } finally {
+ // If we get here we are obviously not stuck reading the stream so
+ // we can cancel the timeout job.
+ // Note that it may already have executed, but that is not a
+ // problem.
+ if (timeoutJob != null) {
+ timeoutJob.cancel();
+ }
+
+ if (process != null) {
+ process.destroy();
+ }
+ }
+ }
+
+ /**
+ * Read from the specified stream and return what was read.
+ *
+ * @param stream
+ * The input stream to be used to read the data. This method will
+ * close the stream.
+ * @return The data read from the stream
+ * @throws IOException
+ * If an IOException happens when reading the stream
+ */
+ private static String readStream(InputStream stream) throws IOException {
+ StringBuilder cmdOutput = new StringBuilder(200);
+ try {
+ Reader r = new InputStreamReader(stream);
+ BufferedReader reader = new BufferedReader(r);
+
+ String line;
+ while ((line = reader.readLine()) != null) {
+ cmdOutput.append(line);
+ cmdOutput.append('\n');
+ }
+ return cmdOutput.toString();
+ } finally {
+ // Cleanup to avoid leaking pipes
+ // Bug 345164
+ if (stream != null) {
+ try {
+ stream.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+
+ private static class IntegerTuple implements Comparable<IntegerTuple> {
+ private Integer[] fIntegers;
+
+ private IntegerTuple(Integer... integers) {
+ fIntegers = integers;
+ }
+
+ @Override
+ public int compareTo(IntegerTuple o) {
+ for (int i = 0; i < fIntegers.length; i++) {
+ if (i >= o.fIntegers.length) {
+ // All numbers are the same up to now but the other tuple
+ // has less
+ return 1;
+ }
+
+ int compareTo = fIntegers[i].compareTo(o.fIntegers[i]);
+ if (compareTo != 0) {
+ return compareTo;
+ }
+ }
+
+ // All numbers are the same up to now but this tuple has less than
+ // the other
+ if (fIntegers.length < o.fIntegers.length) {
+ return -1;
+ }
+
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < fIntegers.length; i++) {
+ sb.append(fIntegers[i]);
+ if (i != fIntegers.length - 1) {
+ sb.append("."); //$NON-NLS-1$
+ }
+ }
+ return sb.toString();
+ }
+ }
+
+ /**
+ * This depends on the SVN revision, for example 350.0.21.9
+ *
+ * @param versionOutput
+ * output text from "lldb --version" command .
+ * @return String representation of revision of lldb such as "350.0.21.9" on
+ * success; null otherwise.
+ */
+ private static IntegerTuple getLLDBRevisionFromText(String versionOutput) {
+ // These are the LLDB version patterns I have seen up to now
+ // Apple Xcode 7.3.1: lldb-350.0.21.9
+ // LLVM build: lldb-360.99.0
+
+ Matcher matcher = LLDB_REVISION_PATTERN.matcher(versionOutput);
+ if (!matcher.find()) {
+ return null;
+ }
+
+ try {
+ Integer major = Integer.valueOf(matcher.group(1));
+ Integer minor = Integer.valueOf(matcher.group(2));
+ Integer micro = Integer.valueOf(matcher.group(3));
+ Integer patch = matcher.groupCount() < 5 ? null : Integer.valueOf(matcher.group(5));
+ IntegerTuple revision = new IntegerTuple(major, minor, micro, patch);
+ return revision;
+ } catch (NumberFormatException e) {
+ LLDBCorePlugin.log(e);
+ }
+ return null;
+ }
+
+ /**
+ * Returns Clang-style/LLVM version, for example 3.9.0
+ *
+ * @param versionOutput
+ * output text from "lldb --version" command .
+ * @return String representation of version of lldb such as "3.9.0" on
+ * success; null otherwise.
+ */
+ private static IntegerTuple getLLDBVersionFromText(String versionOutput) {
+ // These are the LLDB version patterns I have seen up to now
+ // Ubuntu 14.04: lldb version 3.6.0 ( revision )
+ // Ubuntu 14.04: lldb version 3.8.0 ( revision )
+
+ Matcher matcher = LLDB_VERSION_PATTERN.matcher(versionOutput);
+ if (!matcher.find()) {
+ return null;
+ }
+
+ try {
+ Integer major = Integer.valueOf(matcher.group(1));
+ Integer minor = Integer.valueOf(matcher.group(2));
+ Integer micro = Integer.valueOf(matcher.group(3));
+ IntegerTuple version = new IntegerTuple(major, minor, micro);
+ return version;
+ } catch (NumberFormatException e) {
+ LLDBCorePlugin.log(e);
+ }
+ return null;
+ }
+
private static String getDefaultLLDBPath() {
return Platform.getPreferencesService().getString(LLDBCorePlugin.PLUGIN_ID,
ILLDBDebugPreferenceConstants.PREF_DEFAULT_LLDB_COMMAND,
diff --git a/llvm/org.eclipse.cdt.llvm.dsf.lldb.core/src/org/eclipse/cdt/llvm/dsf/lldb/core/internal/launching/Messages.java b/llvm/org.eclipse.cdt.llvm.dsf.lldb.core/src/org/eclipse/cdt/llvm/dsf/lldb/core/internal/launching/Messages.java
index 52e630d874d..8f629b1c034 100644
--- a/llvm/org.eclipse.cdt.llvm.dsf.lldb.core/src/org/eclipse/cdt/llvm/dsf/lldb/core/internal/launching/Messages.java
+++ b/llvm/org.eclipse.cdt.llvm.dsf.lldb.core/src/org/eclipse/cdt/llvm/dsf/lldb/core/internal/launching/Messages.java
@@ -16,6 +16,7 @@ import org.eclipse.osgi.util.NLS;
@SuppressWarnings("javadoc")
public class Messages extends NLS {
private static final String BUNDLE_NAME = "org.eclipse.cdt.llvm.dsf.lldb.core.internal.launching.messages"; //$NON-NLS-1$
+ public static String LLDBLaunch_minimum_version_error;
public static String LLDBLaunchDelegate_mimicking_gdb;
static {
// initialize resource bundle
diff --git a/llvm/org.eclipse.cdt.llvm.dsf.lldb.core/src/org/eclipse/cdt/llvm/dsf/lldb/core/internal/launching/messages.properties b/llvm/org.eclipse.cdt.llvm.dsf.lldb.core/src/org/eclipse/cdt/llvm/dsf/lldb/core/internal/launching/messages.properties
index d0cd1c9abad..bf06011ab39 100644
--- a/llvm/org.eclipse.cdt.llvm.dsf.lldb.core/src/org/eclipse/cdt/llvm/dsf/lldb/core/internal/launching/messages.properties
+++ b/llvm/org.eclipse.cdt.llvm.dsf.lldb.core/src/org/eclipse/cdt/llvm/dsf/lldb/core/internal/launching/messages.properties
@@ -6,4 +6,5 @@
# http://www.eclipse.org/legal/epl-v10.html
###############################################################################
+LLDBLaunch_minimum_version_error=The lldb debugger does not meet the minimum version requirement. Current: {0}, required: {1} {2}.
LLDBLaunchDelegate_mimicking_gdb=mimicking

Back to the top