diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/launch/NdkGdbLaunchDelegate.java')
-rw-r--r-- | eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/launch/NdkGdbLaunchDelegate.java | 506 |
1 files changed, 0 insertions, 506 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/launch/NdkGdbLaunchDelegate.java b/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/launch/NdkGdbLaunchDelegate.java deleted file mode 100644 index 0b124f249..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.ndk/src/com/android/ide/eclipse/ndk/internal/launch/NdkGdbLaunchDelegate.java +++ /dev/null @@ -1,506 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php - * - * 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.ndk.internal.launch; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.ddmlib.AdbCommandRejectedException; -import com.android.ddmlib.AndroidDebugBridge; -import com.android.ddmlib.Client; -import com.android.ddmlib.CollectingOutputReceiver; -import com.android.ddmlib.IDevice; -import com.android.ddmlib.IDevice.DeviceUnixSocketNamespace; -import com.android.ddmlib.InstallException; -import com.android.ddmlib.ShellCommandUnresponsiveException; -import com.android.ddmlib.SyncException; -import com.android.ddmlib.TimeoutException; -import com.android.ide.common.xml.ManifestData; -import com.android.ide.common.xml.ManifestData.Activity; -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestInfo; -import com.android.ide.eclipse.adt.internal.launch.AndroidLaunchController; -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.ndk.internal.NativeAbi; -import com.android.ide.eclipse.ndk.internal.NdkHelper; -import com.android.ide.eclipse.ndk.internal.NdkVariables; -import com.android.sdklib.AndroidVersion; -import com.android.sdklib.IAndroidTarget; -import com.google.common.base.Joiner; - -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.Collection; -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 { - public static final String LAUNCH_TYPE_ID = - "com.android.ide.eclipse.ndk.debug.LaunchConfigType"; //$NON-NLS-1$ - - private static final Joiner JOINER = Joiner.on(", ").skipNulls(); - - 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(final 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); - final ManifestInfo manifestInfo = ManifestInfo.get(project); - final AndroidVersion minSdkVersion = new AndroidVersion( - manifestInfo.getMinSdkVersion(), - manifestInfo.getMinSdkCodeName()); - - // 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); - Collection<NativeAbi> appAbis = NdkHelper.getApplicationAbis(project, monitor); - if (appAbis.size() == 0) { - AdtPlugin.printErrorToConsole(project, - Messages.NdkGdbLaunchDelegate_LaunchError_UnableToDetectAppAbi); - return false; - } - - // Obtain device to use: - // - if there is only 1 device, just use that - // - if we have previously launched this config, and the device used is present, use that - // - otherwise show the DeviceChooserDialog - final String configName = config.getName(); - monitor.setTaskName(Messages.NdkGdbLaunchDelegate_Action_ObtainDevice); - IDevice device = null; - IDevice[] devices = AndroidDebugBridge.getBridge().getDevices(); - if (devices.length == 1) { - device = devices[0]; - } else if ((device = getLastUsedDevice(config, devices)) == null) { - 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, minSdkVersion, false /*** FIXME! **/); - if (dialog.open() == Dialog.OK) { - AndroidLaunchController.updateLaunchConfigWithLastUsedDevice(config, - response); - continueLaunch[0] = true; - } - }; - }); - - if (!continueLaunch[0]) { - return false; - } - - 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); - AdtPlugin.printErrorToConsole(project, - String.format("ABI's supported by the application: %s", JOINER.join(appAbis))); - AdtPlugin.printErrorToConsole(project, - String.format("ABI's supported by the device: %s, %s", //$NON-NLS-1$ - deviceAbi1, - deviceAbi2)); - 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); - CollectingOutputReceiver receiver = new CollectingOutputReceiver(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.getAbi()); - 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()); - } - AdtPlugin.printErrorToConsole(project, - Messages.NdkGdbLaunchDelegate_LaunchError_VerifyIfDebugBuild); - - // 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, - NdkLaunchConstants.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 - ILaunchConfiguration config2 = performVariableSubstitutions(config, project, compatAbi, - monitor); - - // launch gdb - monitor.setTaskName(Messages.NdkGdbLaunchDelegate_Action_LaunchHostGdb); - super.launch(config2, mode, launch, monitor); - return true; - } - - @Nullable - private IDevice getLastUsedDevice(ILaunchConfiguration config, @NonNull IDevice[] devices) { - try { - boolean reuse = config.getAttribute(LaunchConfigDelegate.ATTR_REUSE_LAST_USED_DEVICE, - false); - if (!reuse) { - return null; - } - - String serial = config.getAttribute(LaunchConfigDelegate.ATTR_LAST_USED_DEVICE, - (String)null); - return AndroidLaunchController.getDeviceIfOnline(serial, devices); - } catch (CoreException e) { - return null; - } - } - - 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.getAbi()); - - IValueVariable[] ndkVars = new IValueVariable[] { ndkGdb, ndkProject, ndkCompatAbi }; - manager.addVariables(ndkVars); - - // fix path to gdb - String userGdbPath = wcopy.getAttribute(NdkLaunchConstants.ATTR_NDK_GDB, - NdkLaunchConstants.DEFAULT_GDB); - wcopy.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUG_NAME, - elaborateExpression(manager, userGdbPath)); - - // setup program name - wcopy.setAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, - elaborateExpression(manager, NdkLaunchConstants.DEFAULT_PROGRAM)); - - // fix solib paths - List<String> solibPaths = wcopy.getAttribute( - NdkLaunchConstants.ATTR_NDK_SOLIB, - Collections.singletonList(NdkLaunchConstants.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, - Collection<NativeAbi> appAbis) { - for (NativeAbi abi: appAbis) { - if (abi.getAbi().equals(deviceAbi1) || abi.getAbi().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); - CollectingOutputReceiver receiver = new CollectingOutputReceiver(commandCompleteLatch); - device.executeShellCommand(command, receiver); - commandCompleteLatch.await(timeout, timeoutUnit); - return receiver.getOutput().trim(); - } -} |