aboutsummaryrefslogtreecommitdiffstats
path: root/eclipse
diff options
context:
space:
mode:
authorSiva Velusamy <vsiva@google.com>2012-03-19 19:24:32 -0700
committerSiva Velusamy <vsiva@google.com>2012-03-26 15:45:19 -0700
commit96a3d5dbf40cc1ee5f0ccfc1cc4507a44e5b2247 (patch)
tree324234ddd7822203a35f9b23c07d90d0a2b39ffd /eclipse
parent805a03988e828ff574ab02e9b7d812b07b493156 (diff)
downloadsdk-96a3d5dbf40cc1ee5f0ccfc1cc4507a44e5b2247.zip
sdk-96a3d5dbf40cc1ee5f0ccfc1cc4507a44e5b2247.tar.gz
sdk-96a3d5dbf40cc1ee5f0ccfc1cc4507a44e5b2247.tar.bz2
Add support for debugging native code.
This CL adds necessary Eclipse support to provide native code debugging. Native debugging is simply a wrapper around CDT DSF's remote process debugging via a gdbserver. Change-Id: Ifeb7c5d31dd94e49f0d5266dd881b0f60532ad9a
Diffstat (limited to 'eclipse')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt.ndk/.classpath3
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt.ndk/META-INF/MANIFEST.MF10
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt.ndk/icons/android_app.pngbin0 -> 454 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt.ndk/plugin.xml30
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/NativeAbi.java32
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/NdkHelper.java135
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/NdkVariables.java31
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/GdbServerTask.java118
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/Messages.java61
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/NdkDebuggerConfigTab.java63
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/NdkGdbLaunchConfigTabGroups.java39
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/NdkGdbLaunchDelegate.java485
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/NdkMainLaunchConfigTab.java61
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/messages.properties31
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/MainLaunchConfigTab.java8
15 files changed, 1103 insertions, 4 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt.ndk/.classpath b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/.classpath
index 1fa3e68..d37aeb2 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt.ndk/.classpath
+++ b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/.classpath
@@ -3,5 +3,8 @@
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/ddmlib"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/ddmuilib"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/SdkLib"/>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt.ndk/META-INF/MANIFEST.MF b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/META-INF/MANIFEST.MF
index 40400bc..35e8537 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt.ndk/META-INF/MANIFEST.MF
+++ b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/META-INF/MANIFEST.MF
@@ -7,6 +7,7 @@ Bundle-Activator: com.android.ide.eclipse.adt.ndk.internal.Activator
Bundle-Vendor: The Android Open Source Project
Require-Bundle: org.eclipse.core.runtime,
org.eclipse.core.resources,
+ org.eclipse.core.variables,
org.eclipse.ui,
org.eclipse.debug.core,
org.eclipse.debug.ui,
@@ -14,8 +15,13 @@ Require-Bundle: org.eclipse.core.runtime,
org.eclipse.cdt.ui,
org.eclipse.cdt.managedbuilder.core,
org.eclipse.cdt.managedbuilder.ui,
- org.eclipse.cdt.dsf.gdb,
+ org.eclipse.cdt.debug.core,
org.eclipse.cdt.debug.ui,
+ org.eclipse.cdt.dsf,
+ org.eclipse.cdt.dsf.gdb,
+ org.eclipse.cdt.dsf.gdb.ui,
org.eclipse.cdt.launch,
- com.android.ide.eclipse.adt
+ com.android.ide.eclipse.adt,
+ com.android.ide.eclipse.ddms
Bundle-ActivationPolicy: lazy
+Bundle-ClassPath: .
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt.ndk/icons/android_app.png b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/icons/android_app.png
new file mode 100644
index 0000000..8ca3770
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/icons/android_app.png
Binary files differ
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt.ndk/plugin.xml b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/plugin.xml
index 5179dd6..591092f 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt.ndk/plugin.xml
+++ b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/plugin.xml
@@ -139,5 +139,35 @@
class="com.android.ide.eclipse.adt.ndk.internal.preferences.NdkPreferenceInitializer">
</initializer>
</extension>
+ <extension
+ point="org.eclipse.debug.core.launchConfigurationTypes">
+ <launchConfigurationType
+ delegate="com.android.ide.eclipse.adt.ndk.internal.launch.NdkGdbLaunchDelegate"
+ delegateName="Android ndk-gdb Launcher"
+ id="com.android.ide.eclipse.adt.ndk.debug.LaunchConfigType"
+ modes="debug"
+ name="Android Native Application"
+ public="true"
+ sourceLocatorId="org.eclipse.cdt.debug.core.sourceLocator"
+ sourcePathComputerId="org.eclipse.cdt.debug.core.sourcePathComputer">
+ </launchConfigurationType>
+ </extension>
+ <extension
+ point="org.eclipse.debug.ui.launchConfigurationTypeImages">
+ <launchConfigurationTypeImage
+ configTypeID="com.android.ide.eclipse.adt.ndk.debug.LaunchConfigType"
+ icon="icons/android_app.png"
+ id="com.android.ide.eclipse.adt.ndk.debug.LaunchConfigTypeImage">
+ </launchConfigurationTypeImage>
+ </extension>
+ <extension
+ point="org.eclipse.debug.ui.launchConfigurationTabGroups">
+ <launchConfigurationTabGroup
+ class="com.android.ide.eclipse.adt.ndk.internal.launch.NdkGdbLaunchConfigTabGroups"
+ description="Android Native Application"
+ id="com.android.ide.eclipse.adt.ndk.debug.LaunchConfigTabGroup"
+ type="com.android.ide.eclipse.adt.ndk.debug.LaunchConfigType">
+ </launchConfigurationTabGroup>
+ </extension>
</plugin>
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/NativeAbi.java b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/NativeAbi.java
new file mode 100644
index 0000000..ccaf412
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/NativeAbi.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.ndk.internal;
+
+public enum NativeAbi {
+ armeabi("armeabi"),
+ x86("x86");
+
+ private final String mAbi;
+
+ private NativeAbi(String abi) {
+ mAbi = abi;
+ }
+
+ public String getAbi() {
+ return mAbi;
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/NdkHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/NdkHelper.java
new file mode 100644
index 0000000..b44f216
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/NdkHelper.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.ndk.internal;
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+
+import org.eclipse.cdt.core.CommandLauncher;
+import org.eclipse.cdt.core.ICommandLauncher;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Path;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+@SuppressWarnings("restriction")
+public class NdkHelper {
+ private static final String MAKE = "make";
+ private static final String CORE_MAKEFILE_PATH = "/build/core/build-local.mk";
+
+ /**
+ * Obtain the ABI's the application is compatible with.
+ * The ABI's are obtained by reading the result of the following command:
+ * make --no-print-dir -f ${NdkRoot}/build/core/build-local.mk -C <project-root> DUMP_APP_ABI
+ */
+ public static List<NativeAbi> getApplicationAbis(IProject project, IProgressMonitor monitor) {
+ ICommandLauncher launcher = new CommandLauncher();
+ launcher.setProject(project);
+ String[] args = new String[] {
+ "--no-print-dir",
+ "-f",
+ NdkManager.getNdkLocation() + CORE_MAKEFILE_PATH,
+ "-C",
+ project.getLocation().toOSString(),
+ "DUMP_APP_ABI",
+ };
+ try {
+ launcher.execute(getPathToMake(), args, null, project.getLocation(), monitor);
+ } catch (CoreException e) {
+ AdtPlugin.printErrorToConsole(e.getLocalizedMessage());
+ return Collections.emptyList();
+ }
+
+ ByteArrayOutputStream stdout = new ByteArrayOutputStream();
+ ByteArrayOutputStream stderr = new ByteArrayOutputStream();
+ launcher.waitAndRead(stdout, stderr, monitor);
+
+ String abis = stdout.toString().trim();
+ List<NativeAbi> nativeAbis = new ArrayList<NativeAbi>(3);
+
+ for (String abi: abis.split(" ")) {
+ try {
+ nativeAbis.add(NativeAbi.valueOf(abi));
+ } catch (IllegalArgumentException e) {
+
+ }
+ }
+
+ return nativeAbis;
+ }
+
+ /**
+ * Obtain the toolchain prefix to use for given project and abi.
+ * The prefix is obtained by reading the result of:
+ * make --no-print-dir -f ${NdkRoot}/build/core/build-local.mk \
+ * -C <project-root> \
+ * DUMP_TOOLCHAIN_PREFIX APP_ABI=abi
+ */
+ public static String getToolchainPrefix(IProject project, NativeAbi abi,
+ IProgressMonitor monitor) {
+ ICommandLauncher launcher = new CommandLauncher();
+ launcher.setProject(project);
+ String[] args = new String[] {
+ "--no-print-dir",
+ "-f",
+ NdkManager.getNdkLocation() + CORE_MAKEFILE_PATH,
+ "-C",
+ project.getLocation().toOSString(),
+ "DUMP_TOOLCHAIN_PREFIX",
+ };
+ try {
+ launcher.execute(getPathToMake(), args, null, project.getLocation(), monitor);
+ } catch (CoreException e) {
+ AdtPlugin.printErrorToConsole(e.getLocalizedMessage());
+ return null;
+ }
+
+ ByteArrayOutputStream stdout = new ByteArrayOutputStream();
+ ByteArrayOutputStream stderr = new ByteArrayOutputStream();
+ launcher.waitAndRead(stdout, stderr, monitor);
+ return stdout.toString().trim();
+ }
+
+ private static IPath getPathToMake() {
+ return getUtilitiesFolder().append(MAKE);
+ }
+
+ /**
+ * Obtain a path to the utilities prebult folder in NDK. This is typically
+ * "${NdkRoot}/prebuilt/<platform>/"
+ */
+ private static synchronized IPath getUtilitiesFolder() {
+ IPath ndkRoot = new Path(NdkManager.getNdkLocation());
+ IPath prebuilt = ndkRoot.append("prebuilt"); //$NON-NLS-1$
+ if (!prebuilt.toFile().exists() || !prebuilt.toFile().canRead()) {
+ return ndkRoot;
+ }
+
+ File[] platforms = prebuilt.toFile().listFiles();
+ if (platforms.length == 1) {
+ return prebuilt.append(platforms[0].getName()).append("bin"); //$NON-NLS-1$
+ }
+
+ return ndkRoot;
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/NdkVariables.java b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/NdkVariables.java
new file mode 100644
index 0000000..3e95c79
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/NdkVariables.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.ndk.internal;
+
+/** Eclipse variables that are understood by the NDK while launching programs. */
+public class NdkVariables {
+ /** Variable that expands to the full path of NDK's ABI specific gdb. */
+ public static final String NDK_GDB = "NdkGdb";
+
+ /** Variable that expands to point to the full path of the project used in the launch
+ * configuration. */
+ public static final String NDK_PROJECT = "NdkProject";
+
+ /** Variable that indicates the ABI that is compatible between the device and the
+ * application being launched. */
+ public static final String NDK_COMPAT_ABI = "NdkCompatAbi";
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/GdbServerTask.java b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/GdbServerTask.java
new file mode 100644
index 0000000..7d58bd9
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/GdbServerTask.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.ndk.internal.launch;
+
+import com.android.ddmlib.IDevice;
+import com.android.ddmlib.IShellOutputReceiver;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * The {@link GdbServerTask} launches gdbserver on the given device and attaches it to
+ * provided pid.
+ */
+public class GdbServerTask implements Runnable {
+ private IDevice mDevice;
+ private String mRunAs;
+ private String mSocket;
+ private int mPid;
+ private CountDownLatch mAttachLatch;
+
+ private GdbServerOutputReceiver mOutputReceiver;
+ private Exception mLaunchException;
+
+ private AtomicBoolean mCancelled = new AtomicBoolean(false);
+ private AtomicBoolean mHasCompleted = new AtomicBoolean(false);
+
+ /**
+ * Construct a gdbserver task.
+ * @param device device to run gdbserver on
+ * @param runAsPackage name of the package in which gdbserver resides
+ * @param socketName name of the local socket on which the server will listen
+ * @param pid pid of task to attach to
+ * @param attachLatch latch to notify when gdbserver gets attached to the task
+ */
+ public GdbServerTask(IDevice device, String runAsPackage, String socketName, int pid,
+ CountDownLatch attachLatch) {
+ mDevice = device;
+ mRunAs = runAsPackage;
+ mSocket = socketName;
+ mPid = pid;
+ mAttachLatch = attachLatch;
+
+ mOutputReceiver = new GdbServerOutputReceiver();
+ }
+
+ /**
+ * Runs gdbserver on the device and connects to the given task. If gdbserver manages to
+ * successfully attach itself to the process, then it counts down on its attach latch.
+ */
+ @Override
+ public void run() {
+ // Launch gdbserver on the device.
+ String command = String.format("run-as %s lib/gdbserver +%s --attach %d",
+ mRunAs, mSocket, mPid);
+ try {
+ mDevice.executeShellCommand(command, mOutputReceiver, 0);
+ } catch (Exception e) {
+ mLaunchException = e;
+ }
+ }
+
+ /** Returns any exceptions that might have occurred while launching gdbserver. */
+ public Exception getLaunchException() {
+ return mLaunchException;
+ }
+
+ /** Cancel gdbserver if it is running. */
+ public void setCancelled() {
+ mCancelled.set(true);
+ }
+
+ public String getShellOutput() {
+ return mOutputReceiver.getOutput();
+ }
+
+ private class GdbServerOutputReceiver implements IShellOutputReceiver {
+ private StringBuffer mOutput = new StringBuffer(100);
+
+ @Override
+ public synchronized void addOutput(byte[] data, int offset, int length) {
+ mOutput.append(new String(data, offset, length));
+
+ // notify other threads that gdbserver has attached to the task
+ if (mOutput.toString().contains("Attached")) {
+ mAttachLatch.countDown();
+ }
+ }
+
+ @Override
+ public void flush() {
+ mHasCompleted.set(true);
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return mCancelled.get();
+ }
+
+ public synchronized String getOutput() {
+ return mOutput.toString();
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/Messages.java b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/Messages.java
new file mode 100644
index 0000000..52112cd
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/Messages.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.ndk.internal.launch;
+
+import org.eclipse.osgi.util.NLS;
+
+public class Messages extends NLS {
+ private static final String BUNDLE_NAME = "com.android.ide.eclipse.adt.ndk.internal.launch.messages"; //$NON-NLS-1$
+ public static String NdkGdbLaunchDelegate_LaunchError_gdbserverOutput;
+ public static String NdkGdbLaunchDelegate_Action_ActivityLaunch;
+ public static String NdkGdbLaunchDelegate_Action_CheckAndroidDeviceVersion;
+ public static String NdkGdbLaunchDelegate_Action_KillExistingGdbServer;
+ public static String NdkGdbLaunchDelegate_Action_LaunchHostGdb;
+ public static String NdkGdbLaunchDelegate_Action_LaunchingGdbServer;
+ public static String NdkGdbLaunchDelegate_Action_ObtainAppAbis;
+ public static String NdkGdbLaunchDelegate_Action_ObtainDevice;
+ public static String NdkGdbLaunchDelegate_Action_ObtainDeviceABI;
+ public static String NdkGdbLaunchDelegate_Action_PerformIncrementalBuild;
+ public static String NdkGdbLaunchDelegate_Action_SettingUpPortForward;
+ public static String NdkGdbLaunchDelegate_Action_SyncAppToDevice;
+ public static String NdkGdbLaunchDelegate_Action_WaitGdbServerAttach;
+ public static String NdkGdbLaunchDelegate_Action_WaitingForActivity;
+ public static String NdkGdbLaunchDelegate_LaunchError_ActivityLaunchError;
+ public static String NdkGdbLaunchDelegate_LaunchError_Api8Needed;
+ public static String NdkGdbLaunchDelegate_LaunchError_CouldNotGetProject;
+ public static String NdkGdbLaunchDelegate_LaunchError_gdbserverLaunchException;
+ public static String NdkGdbLaunchDelegate_LaunchError_InstallError;
+ public static String NdkGdbLaunchDelegate_LaunchError_InterruptedWaitingForGdbserver;
+ public static String NdkGdbLaunchDelegate_LaunchError_NoActivityInManifest;
+ public static String NdkGdbLaunchDelegate_LaunchError_NoCompatibleAbi;
+ public static String NdkGdbLaunchDelegate_LaunchError_NoLauncherActivity;
+ public static String NdkGdbLaunchDelegate_LaunchError_NoSuchActivity;
+ public static String NdkGdbLaunchDelegate_LaunchError_NullApk;
+ public static String NdkGdbLaunchDelegate_LaunchError_ObtainingAppFolder;
+ public static String NdkGdbLaunchDelegate_LaunchError_PortForwarding;
+ public static String NdkGdbLaunchDelegate_LaunchError_ProjectHasErrors;
+ public static String NdkGdbLaunchDelegate_LaunchError_PullFileError;
+ public static String NdkGdbLaunchDelegate_LaunchError_UnableToDetectAppAbi;
+ public static String NdkGdbLaunchDelegate_LaunchError_UnknownAndroidDeviceVersion;
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+ }
+
+ private Messages() {
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/NdkDebuggerConfigTab.java b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/NdkDebuggerConfigTab.java
new file mode 100644
index 0000000..cd68b6a
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/NdkDebuggerConfigTab.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.ndk.internal.launch;
+
+import com.android.ide.eclipse.adt.ndk.internal.NdkVariables;
+
+import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
+import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants;
+import org.eclipse.cdt.dsf.gdb.internal.ui.launching.RemoteApplicationCDebuggerTab;
+import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@SuppressWarnings("restriction")
+public class NdkDebuggerConfigTab extends RemoteApplicationCDebuggerTab {
+ public static final String DEFAULT_GDB_PORT = "5039";
+ public static final String DEFAULT_GDB = getVar(NdkVariables.NDK_GDB);
+ public static final String DEFAULT_PROGRAM =
+ String.format("%1$s/obj/local/%2$s/app_process",
+ getVar(NdkVariables.NDK_PROJECT),
+ getVar(NdkVariables.NDK_COMPAT_ABI));
+ public static final String DEFAULT_SOLIB_PATH =
+ getVar(NdkVariables.NDK_PROJECT) + "/obj/local/armeabi/";
+
+
+ @Override
+ public void setDefaults(ILaunchConfigurationWorkingCopy config) {
+ super.setDefaults(config);
+
+ config.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUG_NAME, DEFAULT_GDB);
+ config.setAttribute(IGDBLaunchConfigurationConstants.ATTR_GDB_INIT, ""); //$NON-NLS-1$
+ config.setAttribute(IGDBLaunchConfigurationConstants.ATTR_PORT, DEFAULT_GDB_PORT);
+ config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN, false);
+
+ config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_START_MODE,
+ IGDBLaunchConfigurationConstants.DEBUGGER_MODE_REMOTE_ATTACH);
+ config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME,
+ DEFAULT_PROGRAM);
+
+ List<String> solibPaths = new ArrayList<String>(2);
+ solibPaths.add(DEFAULT_SOLIB_PATH);
+ config.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_SOLIB_PATH, solibPaths);
+ }
+
+ private static String getVar(String varName) {
+ return "${" + varName + "}";
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/NdkGdbLaunchConfigTabGroups.java b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/NdkGdbLaunchConfigTabGroups.java
new file mode 100644
index 0000000..1882ae5
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/NdkGdbLaunchConfigTabGroups.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.ndk.internal.launch;
+
+import org.eclipse.debug.ui.AbstractLaunchConfigurationTabGroup;
+import org.eclipse.debug.ui.CommonTab;
+import org.eclipse.debug.ui.ILaunchConfigurationDialog;
+import org.eclipse.debug.ui.ILaunchConfigurationTab;
+import org.eclipse.debug.ui.sourcelookup.SourceLookupTab;
+
+public class NdkGdbLaunchConfigTabGroups extends AbstractLaunchConfigurationTabGroup {
+ public NdkGdbLaunchConfigTabGroups() {
+ }
+
+ @Override
+ public void createTabs(ILaunchConfigurationDialog dialog, String mode) {
+ ILaunchConfigurationTab[] tabs = new ILaunchConfigurationTab[] {
+ new NdkMainLaunchConfigTab(),
+ new NdkDebuggerConfigTab(),
+ new SourceLookupTab(),
+ new CommonTab()
+ };
+ setTabs(tabs);
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/NdkGdbLaunchDelegate.java b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/NdkGdbLaunchDelegate.java
new file mode 100644
index 0000000..a456e73
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/NdkGdbLaunchDelegate.java
@@ -0,0 +1,485 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.ndk.internal.launch;
+
+import com.android.ddmlib.AdbCommandRejectedException;
+import com.android.ddmlib.Client;
+import com.android.ddmlib.IDevice;
+import com.android.ddmlib.IDevice.DeviceUnixSocketNamespace;
+import com.android.ddmlib.IShellOutputReceiver;
+import com.android.ddmlib.InstallException;
+import com.android.ddmlib.ShellCommandUnresponsiveException;
+import com.android.ddmlib.SyncException;
+import com.android.ddmlib.TimeoutException;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.launch.DeviceChooserDialog;
+import com.android.ide.eclipse.adt.internal.launch.DeviceChooserDialog.DeviceChooserResponse;
+import com.android.ide.eclipse.adt.internal.launch.LaunchConfigDelegate;
+import com.android.ide.eclipse.adt.internal.project.AndroidManifestHelper;
+import com.android.ide.eclipse.adt.internal.project.ProjectHelper;
+import com.android.ide.eclipse.adt.internal.sdk.Sdk;
+import com.android.ide.eclipse.adt.ndk.internal.NativeAbi;
+import com.android.ide.eclipse.adt.ndk.internal.NdkHelper;
+import com.android.ide.eclipse.adt.ndk.internal.NdkVariables;
+import com.android.sdklib.AndroidVersion;
+import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.xml.ManifestData;
+import com.android.sdklib.xml.ManifestData.Activity;
+
+import org.eclipse.cdt.core.model.ICProject;
+import org.eclipse.cdt.debug.core.CDebugUtils;
+import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
+import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants;
+import org.eclipse.cdt.dsf.gdb.launching.GdbLaunchDelegate;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.variables.IStringVariableManager;
+import org.eclipse.core.variables.IValueVariable;
+import org.eclipse.core.variables.VariablesPlugin;
+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.jface.dialogs.Dialog;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@SuppressWarnings("restriction")
+public class NdkGdbLaunchDelegate extends GdbLaunchDelegate {
+ private static final String DEBUG_SOCKET = "debugsock"; //$NON-NLS-1$
+
+ @Override
+ public void launch(ILaunchConfiguration config, String mode, ILaunch launch,
+ IProgressMonitor monitor) throws CoreException {
+ boolean launched = doLaunch(config, mode, launch, monitor);
+ if (!launched) {
+ if (launch.canTerminate()) {
+ launch.terminate();
+ }
+ DebugPlugin.getDefault().getLaunchManager().removeLaunch(launch);
+ }
+ }
+
+ public boolean doLaunch(ILaunchConfiguration config, String mode, ILaunch launch,
+ IProgressMonitor monitor) throws CoreException {
+ IProject project = null;
+ ICProject cProject = CDebugUtils.getCProject(config);
+ if (cProject != null) {
+ project = cProject.getProject();
+ }
+
+ if (project == null) {
+ AdtPlugin.printErrorToConsole(
+ Messages.NdkGdbLaunchDelegate_LaunchError_CouldNotGetProject);
+ return false;
+ }
+
+ // make sure the project and its dependencies are built and PostCompilerBuilder runs.
+ // This is a synchronous call which returns when the build is done.
+ monitor.setTaskName(Messages.NdkGdbLaunchDelegate_Action_PerformIncrementalBuild);
+ ProjectHelper.doFullIncrementalDebugBuild(project, monitor);
+
+ // check if the project has errors, and abort in this case.
+ if (ProjectHelper.hasError(project, true)) {
+ AdtPlugin.printErrorToConsole(project,
+ Messages.NdkGdbLaunchDelegate_LaunchError_ProjectHasErrors);
+ return false;
+ }
+
+ final ManifestData manifestData = AndroidManifestHelper.parseForData(project);
+
+ // Get the activity name to launch
+ String activityName = getActivityToLaunch(
+ getActivityNameInLaunchConfig(config),
+ manifestData.getLauncherActivity(),
+ manifestData.getActivities(),
+ project);
+
+ // Get ABI's supported by the application
+ monitor.setTaskName(Messages.NdkGdbLaunchDelegate_Action_ObtainAppAbis);
+ List<NativeAbi> appAbis = NdkHelper.getApplicationAbis(project, monitor);
+ if (appAbis.size() == 0) {
+ AdtPlugin.printErrorToConsole(project,
+ Messages.NdkGdbLaunchDelegate_LaunchError_UnableToDetectAppAbi);
+ return false;
+ }
+
+ // Show device chooser dialog and get device to use.
+ monitor.setTaskName(Messages.NdkGdbLaunchDelegate_Action_ObtainDevice);
+ final IAndroidTarget projectTarget = Sdk.getCurrent().getTarget(project);
+ final DeviceChooserResponse response = new DeviceChooserResponse();
+ final boolean continueLaunch[] = new boolean[] { false };
+ AdtPlugin.getDisplay().syncExec(new Runnable() {
+ @Override
+ public void run() {
+ DeviceChooserDialog dialog = new DeviceChooserDialog(
+ AdtPlugin.getDisplay().getActiveShell(),
+ response,
+ manifestData.getPackage(),
+ projectTarget);
+ if (dialog.open() == Dialog.OK) {
+ continueLaunch[0] = true;
+ }
+ };
+ });
+
+ if (!continueLaunch[0]) {
+ return false;
+ }
+
+ IDevice device = response.getDeviceToUse();
+
+ // ndk-gdb requires device > Froyo
+ monitor.setTaskName(Messages.NdkGdbLaunchDelegate_Action_CheckAndroidDeviceVersion);
+ AndroidVersion deviceVersion = Sdk.getDeviceVersion(device);
+ if (deviceVersion == null) {
+ AdtPlugin.printErrorToConsole(project,
+ Messages.NdkGdbLaunchDelegate_LaunchError_UnknownAndroidDeviceVersion);
+ return false;
+ } else if (!deviceVersion.isGreaterOrEqualThan(8)) {
+ AdtPlugin.printErrorToConsole(project,
+ Messages.NdkGdbLaunchDelegate_LaunchError_Api8Needed);
+ return false;
+ }
+
+ // get Device ABI
+ monitor.setTaskName(Messages.NdkGdbLaunchDelegate_Action_ObtainDeviceABI);
+ String deviceAbi1 = device.getProperty("ro.product.cpu.abi"); //$NON-NLS-1$
+ String deviceAbi2 = device.getProperty("ro.product.cpu.abi2"); //$NON-NLS-1$
+
+ // get the abi that is supported by both the device and the application
+ NativeAbi compatAbi = getCompatibleAbi(deviceAbi1, deviceAbi2, appAbis);
+ if (compatAbi == null) {
+ AdtPlugin.printErrorToConsole(project,
+ Messages.NdkGdbLaunchDelegate_LaunchError_NoCompatibleAbi);
+ return false;
+ }
+
+ // sync app
+ monitor.setTaskName(Messages.NdkGdbLaunchDelegate_Action_SyncAppToDevice);
+ IFile apk = ProjectHelper.getApplicationPackage(project);
+ if (apk == null) {
+ AdtPlugin.printErrorToConsole(project,
+ Messages.NdkGdbLaunchDelegate_LaunchError_NullApk);
+ return false;
+ }
+ try {
+ device.installPackage(apk.getLocation().toOSString(), true);
+ } catch (InstallException e1) {
+ AdtPlugin.printErrorToConsole(project,
+ Messages.NdkGdbLaunchDelegate_LaunchError_InstallError, e1);
+ return false;
+ }
+
+ // launch activity
+ monitor.setTaskName(Messages.NdkGdbLaunchDelegate_Action_ActivityLaunch + activityName);
+ String command = String.format("am start -n %s/%s", manifestData.getPackage(), //$NON-NLS-1$
+ activityName);
+ try {
+ CountDownLatch launchedLatch = new CountDownLatch(1);
+ ShellOutputReceiver receiver = new ShellOutputReceiver(launchedLatch);
+ device.executeShellCommand(command, receiver);
+ launchedLatch.await(5, TimeUnit.SECONDS);
+ String shellOutput = receiver.getOutput();
+ if (shellOutput.contains("Error type")) { //$NON-NLS-1$
+ throw new RuntimeException(receiver.getOutput());
+ }
+ } catch (Exception e) {
+ AdtPlugin.printErrorToConsole(project,
+ Messages.NdkGdbLaunchDelegate_LaunchError_ActivityLaunchError, e);
+ return false;
+ }
+
+ // kill existing gdbserver
+ monitor.setTaskName(Messages.NdkGdbLaunchDelegate_Action_KillExistingGdbServer);
+ for (Client c: device.getClients()) {
+ String description = c.getClientData().getClientDescription();
+ if (description != null && description.contains("gdbserver")) { //$NON-NLS-1$
+ c.kill();
+ }
+ }
+
+ // pull app_process & libc from the device
+ IPath solibFolder = project.getLocation().append("obj/local").append(compatAbi.name());
+ try {
+ pull(device, "/system/bin/app_process", solibFolder); //$NON-NLS-1$
+ pull(device, "/system/lib/libc.so", solibFolder); //$NON-NLS-1$
+ } catch (Exception e) {
+ AdtPlugin.printErrorToConsole(project,
+ Messages.NdkGdbLaunchDelegate_LaunchError_PullFileError, e);
+ return false;
+ }
+
+ // wait for a couple of seconds for activity to be launched
+ monitor.setTaskName(Messages.NdkGdbLaunchDelegate_Action_WaitingForActivity);
+ try {
+ Thread.sleep(2000);
+ } catch (InterruptedException e1) {
+ // uninterrupted
+ }
+
+ // get pid of activity
+ Client app = device.getClient(manifestData.getPackage());
+ int pid = app.getClientData().getPid();
+
+ // launch gdbserver
+ monitor.setTaskName(Messages.NdkGdbLaunchDelegate_Action_LaunchingGdbServer);
+ CountDownLatch attachLatch = new CountDownLatch(1);
+ GdbServerTask gdbServer = new GdbServerTask(device, manifestData.getPackage(),
+ DEBUG_SOCKET, pid, attachLatch);
+ new Thread(gdbServer,
+ String.format("gdbserver for %s", manifestData.getPackage())).start(); //$NON-NLS-1$
+
+ // wait for gdbserver to attach
+ monitor.setTaskName(Messages.NdkGdbLaunchDelegate_Action_WaitGdbServerAttach);
+ boolean attached = false;
+ try {
+ attached = attachLatch.await(3, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ AdtPlugin.printErrorToConsole(project,
+ Messages.NdkGdbLaunchDelegate_LaunchError_InterruptedWaitingForGdbserver);
+ return false;
+ }
+
+ // if gdbserver failed to attach, we report any errors that may have occurred
+ if (!attached) {
+ if (gdbServer.getLaunchException() != null) {
+ AdtPlugin.printErrorToConsole(project,
+ Messages.NdkGdbLaunchDelegate_LaunchError_gdbserverLaunchException,
+ gdbServer.getLaunchException());
+ } else {
+ AdtPlugin.printErrorToConsole(project,
+ Messages.NdkGdbLaunchDelegate_LaunchError_gdbserverOutput,
+ gdbServer.getShellOutput());
+ }
+
+ // shut down the gdbserver thread
+ gdbServer.setCancelled();
+ return false;
+ }
+
+ // Obtain application working directory
+ String appDir = null;
+ try {
+ appDir = getAppDirectory(device, manifestData.getPackage(), 5, TimeUnit.SECONDS);
+ } catch (Exception e) {
+ AdtPlugin.printErrorToConsole(project,
+ Messages.NdkGdbLaunchDelegate_LaunchError_ObtainingAppFolder, e);
+ return false;
+ }
+
+ // setup port forwarding between local port & remote (device) unix domain socket
+ monitor.setTaskName(Messages.NdkGdbLaunchDelegate_Action_SettingUpPortForward);
+ String localport = config.getAttribute(IGDBLaunchConfigurationConstants.ATTR_PORT,
+ NdkDebuggerConfigTab.DEFAULT_GDB_PORT);
+ try {
+ device.createForward(Integer.parseInt(localport),
+ String.format("%s/%s", appDir, DEBUG_SOCKET), //$NON-NLS-1$
+ DeviceUnixSocketNamespace.FILESYSTEM);
+ } catch (Exception e) {
+ AdtPlugin.printErrorToConsole(project,
+ Messages.NdkGdbLaunchDelegate_LaunchError_PortForwarding, e);
+ return false;
+ }
+
+ // update launch attributes based on device
+ config = performVariableSubstitutions(config, project, compatAbi, monitor);
+
+ // launch gdb
+ monitor.setTaskName(Messages.NdkGdbLaunchDelegate_Action_LaunchHostGdb);
+ super.launch(config, mode, launch, monitor);
+ return true;
+ }
+
+ private void pull(IDevice device, String remote, IPath solibFolder) throws
+ SyncException, IOException, AdbCommandRejectedException, TimeoutException {
+ String remoteFileName = new Path(remote).toFile().getName();
+ String targetFile = solibFolder.append(remoteFileName).toString();
+ device.pullFile(remote, targetFile);
+ }
+
+ private ILaunchConfiguration performVariableSubstitutions(ILaunchConfiguration config,
+ IProject project, NativeAbi compatAbi, IProgressMonitor monitor) throws CoreException {
+ ILaunchConfigurationWorkingCopy wcopy = config.getWorkingCopy();
+
+ String toolchainPrefix = NdkHelper.getToolchainPrefix(project, compatAbi, monitor);
+ String gdb = toolchainPrefix + "gdb"; //$NON-NLS-1$
+
+ IStringVariableManager manager = VariablesPlugin.getDefault().getStringVariableManager();
+ IValueVariable ndkGdb = manager.newValueVariable(NdkVariables.NDK_GDB,
+ NdkVariables.NDK_GDB, true, gdb);
+ IValueVariable ndkProject = manager.newValueVariable(NdkVariables.NDK_PROJECT,
+ NdkVariables.NDK_PROJECT, true, project.getLocation().toOSString());
+ IValueVariable ndkCompatAbi = manager.newValueVariable(NdkVariables.NDK_COMPAT_ABI,
+ NdkVariables.NDK_COMPAT_ABI, true, compatAbi.name());
+
+ IValueVariable[] ndkVars = new IValueVariable[] { ndkGdb, ndkProject, ndkCompatAbi };
+ manager.addVariables(ndkVars);
+
+ // fix path to gdb
+ String userGdbPath = wcopy.getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUG_NAME,
+ NdkDebuggerConfigTab.DEFAULT_GDB);
+ wcopy.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUG_NAME,
+ elaborateExpression(manager, userGdbPath));
+
+ // fix program name
+ String userProgramPath = wcopy.getAttribute(
+ ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME,
+ NdkDebuggerConfigTab.DEFAULT_PROGRAM);
+ wcopy.setAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME,
+ elaborateExpression(manager, userProgramPath));
+
+ // fix solib paths
+ List<String> solibPaths = wcopy.getAttribute(
+ IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_SOLIB_PATH,
+ Collections.singletonList(NdkDebuggerConfigTab.DEFAULT_SOLIB_PATH));
+ List<String> fixedSolibPaths = new ArrayList<String>(solibPaths.size());
+ for (String u : solibPaths) {
+ fixedSolibPaths.add(elaborateExpression(manager, u));
+ }
+ wcopy.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_SOLIB_PATH,
+ fixedSolibPaths);
+
+ manager.removeVariables(ndkVars);
+
+ return wcopy.doSave();
+ }
+
+ private String elaborateExpression(IStringVariableManager manager, String expr)
+ throws CoreException{
+ boolean DEBUG = true;
+
+ String eval = manager.performStringSubstitution(expr);
+ if (DEBUG) {
+ AdtPlugin.printToConsole("Substitute: ", expr, " --> ", eval);
+ }
+
+ return eval;
+ }
+
+ /**
+ * Returns the activity name to launch. If the user has requested a particular activity to
+ * be launched, then this method will confirm that the requested activity is defined in the
+ * manifest. If the user has not specified any activities, then it returns the default
+ * launcher activity.
+ * @param activityNameInLaunchConfig activity to launch as requested by the user.
+ * @param activities list of activities as defined in the application's manifest
+ * @param project android project
+ * @return activity name that should be launched, or null if no launchable activity.
+ */
+ private String getActivityToLaunch(String activityNameInLaunchConfig, Activity launcherActivity,
+ Activity[] activities, IProject project) {
+ if (activities.length == 0) {
+ AdtPlugin.printErrorToConsole(project,
+ Messages.NdkGdbLaunchDelegate_LaunchError_NoActivityInManifest);
+ return null;
+ } else if (activityNameInLaunchConfig == null && launcherActivity != null) {
+ return launcherActivity.getName();
+ } else {
+ for (Activity a : activities) {
+ if (a != null && a.getName().equals(activityNameInLaunchConfig)) {
+ return activityNameInLaunchConfig;
+ }
+ }
+
+ AdtPlugin.printErrorToConsole(project,
+ Messages.NdkGdbLaunchDelegate_LaunchError_NoSuchActivity);
+ if (launcherActivity != null) {
+ return launcherActivity.getName();
+ } else {
+ AdtPlugin.printErrorToConsole(
+ Messages.NdkGdbLaunchDelegate_LaunchError_NoLauncherActivity);
+ return null;
+ }
+ }
+ }
+
+ private NativeAbi getCompatibleAbi(String deviceAbi1, String deviceAbi2,
+ List<NativeAbi> appAbis) {
+ for (NativeAbi abi: appAbis) {
+ if (abi.toString().equals(deviceAbi1) || abi.toString().equals(deviceAbi2)) {
+ return abi;
+ }
+ }
+
+ return null;
+ }
+
+ /** Returns the name of the activity as defined in the launch configuration. */
+ private String getActivityNameInLaunchConfig(ILaunchConfiguration configuration) {
+ String empty = ""; //$NON-NLS-1$
+ String activityName;
+ try {
+ activityName = configuration.getAttribute(LaunchConfigDelegate.ATTR_ACTIVITY, empty);
+ } catch (CoreException e) {
+ return null;
+ }
+
+ return (activityName != empty) ? activityName : null;
+ }
+
+ private String getAppDirectory(IDevice device, String app, long timeout, TimeUnit timeoutUnit)
+ throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException,
+ IOException, InterruptedException {
+ String command = String.format("run-as %s /system/bin/sh -c pwd", app); //$NON-NLS-1$
+
+ CountDownLatch commandCompleteLatch = new CountDownLatch(1);
+ ShellOutputReceiver receiver = new ShellOutputReceiver(commandCompleteLatch);
+ device.executeShellCommand(command, receiver);
+ commandCompleteLatch.await(timeout, timeoutUnit);
+ return receiver.getOutput().trim();
+ }
+
+ private static class ShellOutputReceiver implements IShellOutputReceiver {
+ private StringBuffer sb = new StringBuffer();
+ private CountDownLatch mCompleteLatch;
+
+ public ShellOutputReceiver(CountDownLatch commandCompleteLatch) {
+ mCompleteLatch = commandCompleteLatch;
+ }
+
+ @Override
+ public void addOutput(byte[] data, int offset, int length) {
+ sb.append(new String(data, offset, length));
+ }
+
+ @Override
+ public void flush() {
+ if (mCompleteLatch != null) {
+ mCompleteLatch.countDown();
+ }
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return false;
+ }
+
+ public String getOutput() {
+ return sb.toString();
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/NdkMainLaunchConfigTab.java b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/NdkMainLaunchConfigTab.java
new file mode 100644
index 0000000..841f1dc
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/NdkMainLaunchConfigTab.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.ndk.internal.launch;
+
+import com.android.ide.eclipse.adt.internal.launch.MainLaunchConfigTab;
+import com.android.ide.eclipse.adt.internal.project.ProjectChooserHelper.IProjectChooserFilter;
+import com.android.ide.eclipse.adt.internal.sdk.ProjectState;
+import com.android.ide.eclipse.adt.internal.sdk.Sdk;
+
+import org.eclipse.cdt.core.model.CoreModel;
+import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+
+@SuppressWarnings("restriction")
+public class NdkMainLaunchConfigTab extends MainLaunchConfigTab {
+ private static class NdkProjectOnlyFilter implements IProjectChooserFilter {
+ @Override
+ public boolean accept(IProject project) {
+ ProjectState state = Sdk.getProjectState(project);
+ if (state == null) {
+ return false;
+ }
+
+ return !state.isLibrary()
+ && (CoreModel.hasCCNature(project) || CoreModel.hasCNature(project));
+ }
+
+ @Override
+ public boolean useCache() {
+ return true;
+ }
+ }
+
+ @Override
+ protected IProjectChooserFilter getProjectFilter() {
+ return new NdkProjectOnlyFilter();
+ }
+
+ @Override
+ public void performApply(ILaunchConfigurationWorkingCopy configuration) {
+ super.performApply(configuration);
+
+ configuration.setAttribute(ICDTLaunchConfigurationConstants.ATTR_PROJECT_NAME,
+ mProjText.getText().trim());
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/messages.properties b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/messages.properties
new file mode 100644
index 0000000..362e625
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt.ndk/src/com/android/ide/eclipse/adt/ndk/internal/launch/messages.properties
@@ -0,0 +1,31 @@
+NdkGdbLaunchDelegate_Action_ActivityLaunch=Launching activity
+NdkGdbLaunchDelegate_Action_CheckAndroidDeviceVersion=Checking Android version on device
+NdkGdbLaunchDelegate_Action_KillExistingGdbServer=Killing existing gdbserver
+NdkGdbLaunchDelegate_Action_LaunchHostGdb=Launching host gdb
+NdkGdbLaunchDelegate_Action_LaunchingGdbServer=Launching gdbserver
+NdkGdbLaunchDelegate_Action_ObtainAppAbis=Obtaining ABI's supported by the application
+NdkGdbLaunchDelegate_Action_ObtainDevice=Obtaining device to use
+NdkGdbLaunchDelegate_Action_ObtainDeviceABI=Obtaining ABI supported by the device
+NdkGdbLaunchDelegate_Action_PerformIncrementalBuild=Performing Incremental Build
+NdkGdbLaunchDelegate_Action_SettingUpPortForward=Setting up port forwarding
+NdkGdbLaunchDelegate_Action_SyncAppToDevice=Syncing application to device
+NdkGdbLaunchDelegate_Action_WaitGdbServerAttach=Waiting for gdbserver to attach to process
+NdkGdbLaunchDelegate_Action_WaitingForActivity=Waiting for activity to be launched
+NdkGdbLaunchDelegate_LaunchError_ActivityLaunchError=Error launching activity
+NdkGdbLaunchDelegate_LaunchError_Api8Needed=Native debugging requires API level 8 or above.
+NdkGdbLaunchDelegate_LaunchError_CouldNotGetProject=Couldn't get project object\!
+NdkGdbLaunchDelegate_LaunchError_gdbserverLaunchException=Exception while launching gdbserver:
+NdkGdbLaunchDelegate_LaunchError_gdbserverOutput=gdbserver output
+NdkGdbLaunchDelegate_LaunchError_InstallError=Installation error
+NdkGdbLaunchDelegate_LaunchError_InterruptedWaitingForGdbserver=Interrupted while waiting for gdbserver to attach
+NdkGdbLaunchDelegate_LaunchError_NoActivityInManifest=The Manifest defines no activity\!
+NdkGdbLaunchDelegate_LaunchError_NoCompatibleAbi=Unable to find a compatible ABI
+NdkGdbLaunchDelegate_LaunchError_NoLauncherActivity=No launcher activity specified. Aborting launch.
+NdkGdbLaunchDelegate_LaunchError_NoSuchActivity=The specified activity does not exist\! Getting the launcher activity.
+NdkGdbLaunchDelegate_LaunchError_NullApk=Null APK
+NdkGdbLaunchDelegate_LaunchError_ObtainingAppFolder=Error while obtaining application data folder on device
+NdkGdbLaunchDelegate_LaunchError_PortForwarding=Error while setting up port forwarding
+NdkGdbLaunchDelegate_LaunchError_ProjectHasErrors=Your project contains error(s), please fix them before running your application.
+NdkGdbLaunchDelegate_LaunchError_PullFileError=Error while obtaining file from device
+NdkGdbLaunchDelegate_LaunchError_UnableToDetectAppAbi=Unable to detect application ABI's
+NdkGdbLaunchDelegate_LaunchError_UnknownAndroidDeviceVersion=Unknown Android version on device.
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/MainLaunchConfigTab.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/MainLaunchConfigTab.java
index 4829c02..094ac63 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/MainLaunchConfigTab.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/MainLaunchConfigTab.java
@@ -19,6 +19,7 @@ package com.android.ide.eclipse.adt.internal.launch;
import com.android.ide.eclipse.adt.internal.editors.IconFactory;
import com.android.ide.eclipse.adt.internal.project.AndroidManifestHelper;
import com.android.ide.eclipse.adt.internal.project.ProjectChooserHelper;
+import com.android.ide.eclipse.adt.internal.project.ProjectChooserHelper.IProjectChooserFilter;
import com.android.ide.eclipse.adt.internal.project.ProjectChooserHelper.NonLibraryProjectOnlyFilter;
import com.android.sdklib.xml.ManifestData;
import com.android.sdklib.xml.ManifestData.Activity;
@@ -112,10 +113,13 @@ public class MainLaunchConfigTab extends AbstractLaunchConfigurationTab {
public MainLaunchConfigTab() {
}
+ protected IProjectChooserFilter getProjectFilter() {
+ return new NonLibraryProjectOnlyFilter();
+ }
+
@Override
public void createControl(Composite parent) {
- mProjectChooserHelper = new ProjectChooserHelper(parent.getShell(),
- new NonLibraryProjectOnlyFilter());
+ mProjectChooserHelper = new ProjectChooserHelper(parent.getShell(), getProjectFilter());
Font font = parent.getFont();
Composite comp = new Composite(parent, SWT.NONE);