/* * Copyright (c) 2012, the Dart project authors. * * Licensed under the Eclipse Public License v1.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/legal/epl-v10.html * * 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.google.dart.tools.debug.core.configs; import com.google.dart.engine.utilities.instrumentation.InstrumentationBuilder; import com.google.dart.tools.core.DartCore; import com.google.dart.tools.core.DartCoreDebug; import com.google.dart.tools.core.model.DartSdkManager; import com.google.dart.tools.core.pub.IPackageRootProvider; import com.google.dart.tools.core.utilities.net.NetUtils; import com.google.dart.tools.debug.core.DartDebugCorePlugin; import com.google.dart.tools.debug.core.DartLaunchConfigWrapper; import com.google.dart.tools.debug.core.DartLaunchConfigurationDelegate; import com.google.dart.tools.debug.core.coverage.CoverageManager; import com.google.dart.tools.debug.core.server.ServerDebugTarget; import com.google.dart.tools.debug.core.server.ServerRemoteProcess; import com.google.dart.tools.debug.core.source.UriToFileResolver; import com.google.dart.tools.debug.core.util.CoreLaunchUtils; import com.google.dart.tools.debug.core.util.IRemoteConnectionDelegate; import org.apache.commons.lang3.ObjectUtils; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Status; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchManager; import org.eclipse.debug.core.model.IDebugTarget; import org.eclipse.debug.core.model.IProcess; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; /** * The Dart Server Application launch configuration. */ public class DartServerLaunchConfigurationDelegate extends DartLaunchConfigurationDelegate implements IRemoteConnectionDelegate { private static final int DEFAULT_PORT_NUMBER = 5858; // private IPackageRootProvider packageRootProvider; private int observatoryPort = -1; /** * Create a new DartServerLaunchConfigurationDelegate. */ public DartServerLaunchConfigurationDelegate() { this(IPackageRootProvider.DEFAULT); } public DartServerLaunchConfigurationDelegate(IPackageRootProvider packageRootProvider) { // this.packageRootProvider = packageRootProvider; } @Override public void doLaunch(ILaunchConfiguration configuration, String mode, ILaunch launch, IProgressMonitor monitor, InstrumentationBuilder instrumentation) throws CoreException { DartLaunchConfigWrapper launchConfig = new DartLaunchConfigWrapper( configuration.getWorkingCopy()); launchConfig.markAsLaunched(); boolean enableDebugging = ILaunchManager.DEBUG_MODE.equals(mode) && !DartCoreDebug.DISABLE_CLI_DEBUGGER; terminateSameLaunches(launch); launchVM(launch, launchConfig, enableDebugging, monitor); } @Override public IDebugTarget performRemoteConnection(String host, int port, IContainer container, IProgressMonitor monitor, boolean usePubServe) throws CoreException { if (monitor == null) { monitor = new NullProgressMonitor(); } ILaunch launch = CoreLaunchUtils.createTemporaryLaunch( DartDebugCorePlugin.SERVER_LAUNCH_CONFIG_ID, host + "[" + port + "]"); monitor.beginTask("Opening Connection...", 1); try { if (container != null) { ILaunchConfiguration launchConfiguration = launch.getLaunchConfiguration(); DartLaunchConfigWrapper wrapper = new DartLaunchConfigWrapper(launchConfiguration); wrapper.setSourceDirectoryName(container.getFullPath().toPortableString()); } CoreLaunchUtils.addLaunch(launch); ServerRemoteProcess process = new ServerRemoteProcess(launch); ServerDebugTarget debugTarget = new ServerDebugTarget(launch, process, host, port); process.setTarget(debugTarget); process.fireCreateEvent(); debugTarget.connect(); monitor.worked(1); launch.addDebugTarget(debugTarget); return debugTarget; } catch (CoreException ce) { CoreLaunchUtils.removeLaunch(launch); throw ce; } finally { monitor.done(); } } protected void launchVM(ILaunch launch, DartLaunchConfigWrapper launchConfig, boolean enableDebugging, IProgressMonitor monitor) throws CoreException { // Usage: dart [options] script.dart [arguments] File currentWorkingDirectory = getCurrentWorkingDirectory(launchConfig); String scriptPath = launchConfig.getApplicationName(); scriptPath = translateToFilePath(currentWorkingDirectory, scriptPath); String vmExecPath = ""; if (DartSdkManager.getManager().hasSdk()) { File vmExec = DartSdkManager.getManager().getSdk().getVmExecutable(); if (vmExec != null) { vmExecPath = vmExec.getAbsolutePath().toString(); } } else { vmExecPath = DartDebugCorePlugin.getPlugin().getDartVmExecutablePath(); } if (vmExecPath.length() == 0) { throw new CoreException( DartDebugCorePlugin.createErrorStatus("The executable path for the Dart VM has not been set.")); } List<String> commandsList = new ArrayList<String>(); int connectionPort = NetUtils.findUnusedPort(DEFAULT_PORT_NUMBER); commandsList.add(vmExecPath); commandsList.addAll(Arrays.asList(launchConfig.getVmArgumentsAsArray())); if (enableDebugging) { commandsList.add("--debug:" + connectionPort); } observatoryPort = NetUtils.findUnusedPort(0); launchConfig.setObservatoryPort(observatoryPort); launchConfig.save(); commandsList.add("--enable-vm-service:" + observatoryPort); commandsList.add("--trace_service_pause_events"); if (launchConfig.getPauseIsolateOnExit()) { commandsList.add("--pause-isolates-on-exit"); } if (launchConfig.getPauseIsolateOnStart()) { commandsList.add("--pause-isolates-on-start"); } // This lets us debug isolates. if (enableDebugging) { commandsList.add("--break-at-isolate-spawn"); } String coverageTempDir = null; if (DartCoreDebug.ENABLE_COVERAGE) { coverageTempDir = CoverageManager.createTempDir(); commandsList.add("--coverage_dir=" + coverageTempDir); } String packageRoot = DartCore.getPlugin().getVmPackageRoot(launchConfig.getProject()); if (packageRoot != null) { String fileSeparator = System.getProperty("file.separator"); if (!packageRoot.endsWith(fileSeparator)) { packageRoot += fileSeparator; } commandsList.add("--package-root=" + packageRoot); } commandsList.add(scriptPath); commandsList.addAll(Arrays.asList(launchConfig.getArgumentsAsArray())); String[] commands = commandsList.toArray(new String[commandsList.size()]); ProcessBuilder processBuilder = new ProcessBuilder(commands); if (currentWorkingDirectory != null) { processBuilder.directory(currentWorkingDirectory); } Process runtimeProcess = null; try { runtimeProcess = processBuilder.start(); if (coverageTempDir != null) { UriToFileResolver uriToFileResolver = new UriToFileResolver(launch); CoverageManager.registerProcess( uriToFileResolver, coverageTempDir, launchConfig.getApplicationName(), runtimeProcess); } } catch (IOException ioe) { throw new CoreException(new Status( IStatus.ERROR, DartDebugCorePlugin.PLUGIN_ID, ioe.getMessage(), ioe)); } IProcess eclipseProcess = null; Map<String, String> processAttributes = new HashMap<String, String>(); String programName = "dart"; processAttributes.put(IProcess.ATTR_PROCESS_TYPE, programName); processAttributes.put(IProcess.ATTR_CMDLINE, describe(processBuilder)); if (runtimeProcess != null) { monitor.beginTask("Dart", IProgressMonitor.UNKNOWN); eclipseProcess = DebugPlugin.newProcess( launch, runtimeProcess, launchConfig.getApplicationName() + " (" + new Date() + ")", processAttributes); } if (runtimeProcess == null || eclipseProcess == null) { if (runtimeProcess != null) { runtimeProcess.destroy(); } throw new CoreException( DartDebugCorePlugin.createErrorStatus("Error starting Dart VM process")); } eclipseProcess.setAttribute(IProcess.ATTR_CMDLINE, describe(processBuilder)); if (enableDebugging) { ServerDebugTarget debugTarget = new ServerDebugTarget(launch, eclipseProcess, connectionPort); try { debugTarget.connect(); launch.addDebugTarget(debugTarget); } catch (DebugException ex) { // We don't throw an exception if the process died before we could connect. if (!isProcessDead(runtimeProcess)) { throw ex; } } } monitor.done(); } private String describe(ProcessBuilder processBuilder) { StringBuilder builder = new StringBuilder(); for (String arg : processBuilder.command()) { builder.append(arg); builder.append(" "); } return builder.toString().trim(); } private File getCurrentWorkingDirectory(DartLaunchConfigWrapper launchConfig) { if (launchConfig.getWorkingDirectory().length() > 0) { String cwd = launchConfig.getWorkingDirectory(); return new File(cwd); } else { IResource resource = launchConfig.getApplicationResource(); if (resource == null) { if (launchConfig.getProject() != null) { return launchConfig.getProject().getLocation().toFile(); } else { return null; } } else { // Set the cwd to the project root. return resource.getProject().getLocation().toFile(); } } } private boolean isProcessDead(Process process) { try { process.exitValue(); return true; } catch (IllegalThreadStateException ex) { return false; } } private void sleep(int millis) { try { Thread.sleep(millis); } catch (InterruptedException e) { } } @SuppressWarnings("deprecation") private void terminateSameLaunches(ILaunch currentLaunch) { ILaunchManager manager = DebugPlugin.getDefault().getLaunchManager(); boolean launchTerminated = false; for (ILaunch launch : manager.getLaunches()) { if (ObjectUtils.equals( launch.getLaunchConfiguration(), currentLaunch.getLaunchConfiguration())) { try { launchTerminated = true; launch.terminate(); } catch (DebugException e) { DartDebugCorePlugin.logError(e); } } } if (launchTerminated) { // Wait a while for processes to shutdown. sleep(100); } } /** * Return either a path relative to the cwd, if possible, or an absolute path to the given script. * * @param cwd the current working directory for the launch * @param scriptPath the path to the script (a workspace path) * @return either a cwd relative path or an absolute path */ private String translateToFilePath(File cwd, String scriptPath) { IResource resource = ResourcesPlugin.getWorkspace().getRoot().findMember(scriptPath); if (resource != null) { String path = resource.getLocation().toFile().getAbsolutePath(); if (cwd != null) { String cwdPath = cwd.getAbsolutePath(); if (!cwdPath.endsWith(File.separator)) { cwdPath = cwdPath + File.separator; } if (path.startsWith(cwdPath)) { path = path.substring(cwdPath.length()); } } return path; } else { return scriptPath; } } }