/******************************************************************************* * Copyright (c) 2013, 2016 Red Hat, Inc. * 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 * * Contributors: * Red Hat Inc. - initial API and implementation * Marc Khouzam (Ericsson) - Update for remote debugging support (bug 450080) *******************************************************************************/ package org.eclipse.cdt.debug.application; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.InvocationTargetException; import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; import org.eclipse.cdt.internal.debug.application.DebugAttachedExecutable; import org.eclipse.cdt.internal.debug.application.DebugCoreFile; import org.eclipse.cdt.internal.debug.application.DebugExecutable; import org.eclipse.cdt.internal.debug.application.DebugRemoteExecutable; import org.eclipse.cdt.internal.debug.application.JobContainer; import org.eclipse.cdt.utils.spawner.ProcessFactory; 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.MultiStatus; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.QualifiedName; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.IJobChangeEvent; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.runtime.jobs.JobChangeAdapter; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.debug.core.ILaunchManager; import org.eclipse.debug.ui.DebugUITools; import org.eclipse.jface.dialogs.ErrorDialog; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.dialogs.ProgressMonitorDialog; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.application.ActionBarAdvisor; import org.eclipse.ui.application.IActionBarConfigurer; import org.eclipse.ui.application.IWorkbenchWindowConfigurer; import org.eclipse.ui.application.WorkbenchWindowAdvisor; public class ApplicationWorkbenchWindowAdvisor extends WorkbenchWindowAdvisor { private static final String STANDALONE_QUALIFIER = "org.eclipse.cdt.debug.application"; //$NON-NLS-1$ private static final String LAST_LAUNCH = "lastLaunch"; //$NON-NLS-1$ private ILaunchConfiguration config; private class StartupException extends FileNotFoundException { private static final long serialVersionUID = 1L; public StartupException(String s) { super(); } } public ApplicationWorkbenchWindowAdvisor(IWorkbenchWindowConfigurer configurer) { super(configurer); } @Override public ActionBarAdvisor createActionBarAdvisor(IActionBarConfigurer configurer) { return new ApplicationActionBarAdvisor(configurer); } @Override public void preWindowOpen() { IWorkbenchWindowConfigurer configurer = getWindowConfigurer(); // configurer.setInitialSize(new Point(400, 300)); configurer.setShowCoolBar(true); configurer.setShowStatusLine(true); configurer.setShowMenuBar(true); configurer.setShowProgressIndicator(true); configurer.setTitle(Messages.Debugger_Title); } @Override public void postWindowCreate() { super.postWindowCreate(); try { IRunnableWithProgress op = new PostWindowCreateRunnable(); new ProgressMonitorDialog(getWindowConfigurer().getWindow().getShell()).run(true, true, op); } catch (InvocationTargetException e) { // handle exception } catch (InterruptedException e) { // handle cancelation } } // Private method to search for executable names on PATH private String findExecutable(String input) { String result = input; Path x = new Path(input); try { if (!x.isAbsolute() && x.segmentCount() == 1) { String command = "which " + input; //$NON-NLS-1$ Process p = null; InputStream in = null; try { p = ProcessFactory.getFactory().exec(command); in = p.getInputStream(); InputStreamReader reader = new InputStreamReader(in); BufferedReader br = new BufferedReader(reader); String line = br.readLine(); if (line != null) result = line; } finally { if (in != null) in.close(); if (p != null) p.destroy(); } } } catch (IOException e) { // do nothing } return result; } public class PostWindowCreateRunnable implements IRunnableWithProgress { @Override public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { monitor.beginTask(Messages.InitializingDebugger, 10); boolean attachExecutable = false; String executable = null; String corefile = null; String buildLog = null; String arguments = null; String remoteAddress = null; String remotePort = null; String pid = null; String[] args = Platform.getCommandLineArgs(); try { for (int i = 0; i < args.length; ++i) { if ("-application".equals(args[i])) //$NON-NLS-1$ i++; // ignore the application specifier else if ("-product".equals(args[i])) //$NON-NLS-1$ i++; // ignore the product specifier else if ("-b".equals(args[i])) { //$NON-NLS-1$ ++i; if (i < args.length) buildLog = args[i]; } else if ("-a".equals(args[i])) { //$NON-NLS-1$ attachExecutable = true; // Make sure 'executable' is still null in case we are dealing with a remote // session that is also an attach, as the -r flag could have been set first executable = null; // Check for optional pid if (i + 1 < args.length) { if (!args[i+1].startsWith("-")) { //$NON-NLS-1$ ++i; pid = args[i]; } } } else if ("-c".equals(args[i])) { //$NON-NLS-1$ ++i; corefile = ""; //$NON-NLS-1$ executable = ""; //$NON-NLS-1$ if (i < args.length) corefile = args[i]; } else if ("-r".equals(args[i])) { //$NON-NLS-1$ ++i; remoteAddress = ""; //$NON-NLS-1$ if (!attachExecutable) executable = ""; //$NON-NLS-1$ if (i < args.length) { String[] params = args[i].split(":"); //$NON-NLS-1$ if (params.length == 2) { remoteAddress = params[0]; remotePort = params[1]; } } } else if ("-e".equals(args[i])) { //$NON-NLS-1$ ++i; if (i < args.length) executable = findExecutable(args[i]); ++i; StringBuilder argBuffer = new StringBuilder(); // Remaining values are arguments to the executable if (i < args.length) argBuffer.append(args[i++]); while (i < args.length) { argBuffer.append(" "); //$NON-NLS-1$ argBuffer.append(args[i++]); } arguments = argBuffer.toString(); } } // Verify any core file or executable path is valid. if (corefile != null) { File executableFile = null; if (executable != null) { executableFile = new File(executable); executable = executableFile.getCanonicalPath(); } File coreFile = new File(corefile); corefile = coreFile.getCanonicalPath(); if (executableFile == null || !executableFile.exists() || !coreFile.exists()) { final CoreFileInfo info = new CoreFileInfo("", "", ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ $NON-NLS-2$ $NON-NLS-3$ final IStatus errorStatus = new Status(IStatus.ERROR, Activator.PLUGIN_ID, 0, Messages.GdbDebugNewExecutableCommand_Binary_file_does_not_exist, null); final String executablePath = executable; final String coreFilePath = corefile; Display.getDefault().syncExec(new Runnable() { @Override public void run() { CoreFileDialog dialog = new CoreFileDialog(getWindowConfigurer().getWindow().getShell(), 0, executablePath, coreFilePath); dialog.setBlockOnOpen(true); if (dialog.open() == IDialogConstants.OK_ID) { CoreFileInfo info2 = dialog.getCoreFileInfo(); info.setHostPath(info2.getHostPath()); info.setCoreFilePath(info2.getCoreFilePath()); } else { ErrorDialog.openError(null, Messages.DebuggerInitializingProblem, null, errorStatus, IStatus.ERROR | IStatus.WARNING); } } }); // Check and see if we failed above and if so, quit if (info.getHostPath().isEmpty()) { monitor.done(); // throw internal exception which will be caught below throw new StartupException(errorStatus.getMessage()); } executable = info.getHostPath(); corefile = info.getCoreFilePath(); } } else if (remoteAddress != null) { // Verify what we can about the port, address and executable. File executableFile = null; if (executable != null) { executableFile = new File(executable); executable = executableFile.getCanonicalPath(); } Integer port = null; try { port = Integer.parseInt(remotePort); } catch (NumberFormatException e) { port = null; } if ((!attachExecutable && (executableFile == null || !executableFile.exists())) || remoteAddress.length() == 0 || port == null) { final RemoteExecutableInfo[] info = new RemoteExecutableInfo[1]; final IStatus errorStatus = new Status(IStatus.ERROR, Activator.PLUGIN_ID, 0, Messages.GdbDebugNewExecutableCommand_Binary_file_does_not_exist, null); final String executablePath = executable; final String addressStr = remoteAddress; final String portStr = remotePort; final String buildLogPath = buildLog; final boolean attach = attachExecutable; Display.getDefault().syncExec(new Runnable() { @Override public void run() { RemoteExecutableDialog dialog = new RemoteExecutableDialog(getWindowConfigurer().getWindow().getShell(), executablePath, buildLogPath, addressStr, portStr, attach); dialog.setBlockOnOpen(true); if (dialog.open() == IDialogConstants.OK_ID) { info[0] = dialog.getExecutableInfo(); } else { info[0] = null; ErrorDialog.openError(null, Messages.DebuggerInitializingProblem, null, errorStatus, IStatus.ERROR | IStatus.WARNING); } } }); // Check and see if we failed above and if so, quit if (info[0] == null) { monitor.done(); // throw internal exception which will be caught below throw new StartupException(errorStatus.getMessage()); } executable = info[0].getHostPath(); buildLog = info[0].getBuildLog(); remoteAddress = info[0].getAddress(); remotePort = info[0].getPort(); attachExecutable = info[0].isAttach(); } } else if (executable != null) { File executableFile = new File(executable); executable = executableFile.getCanonicalPath(); File buildLogFile = null; if (buildLog != null) { buildLogFile = new File(buildLog); buildLog = buildLogFile.getCanonicalPath(); } if (!executableFile.exists() || (buildLogFile != null && !buildLogFile.exists())) { final NewExecutableInfo info = new NewExecutableInfo("", "", "", ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ $NON-NLS-2$ $NON-NLS-3$ final IStatus errorStatus = new Status(IStatus.ERROR, Activator.PLUGIN_ID, 0, Messages.GdbDebugNewExecutableCommand_Binary_file_does_not_exist, null); final String executablePath = executable; final String executableArgs = arguments; final String buildLogPath = buildLog; Display.getDefault().syncExec(new Runnable() { @Override public void run() { NewExecutableDialog dialog = new NewExecutableDialog(getWindowConfigurer().getWindow().getShell(), 0, executablePath, buildLogPath, executableArgs); dialog.setBlockOnOpen(true); if (dialog.open() == IDialogConstants.OK_ID) { NewExecutableInfo info2 = dialog.getExecutableInfo(); info.setHostPath(info2.getHostPath()); info.setArguments(info2.getArguments()); } else { ErrorDialog.openError(null, Messages.DebuggerInitializingProblem, null, errorStatus, IStatus.ERROR | IStatus.WARNING); } } }); // Check and see if we failed above and if so, quit if (info.getHostPath().isEmpty()) { monitor.done(); // throw internal exception which will be caught below throw new StartupException(errorStatus.getMessage()); } executable = info.getHostPath(); arguments = info.getArguments(); } } monitor.worked(1); if (remoteAddress != null && remoteAddress.length() > 0 && remotePort != null && remotePort.length() > 0) { config = DebugRemoteExecutable.createLaunchConfig(monitor, buildLog, executable, remoteAddress, remotePort, attachExecutable); } else if (attachExecutable) { config = DebugAttachedExecutable.createLaunchConfig(monitor, buildLog, pid); } else if (corefile != null && corefile.length() > 0) { config = DebugCoreFile.createLaunchConfig(monitor, buildLog, executable, corefile); } else if (executable != null && executable.length() > 0) { config = DebugExecutable.importAndCreateLaunchConfig(monitor, executable, buildLog, arguments, true); } else { // No executable specified, look for last launch // and offer that to the end-user. monitor.subTask(Messages.RestorePreviousLaunch); String memento = ResourcesPlugin.getWorkspace().getRoot().getPersistentProperty(new QualifiedName(STANDALONE_QUALIFIER, LAST_LAUNCH)); if (memento != null) config = DebugExecutable.getLaunchManager().getLaunchConfiguration(memento); String oldExecutable = ""; //$NON-NLS-1$ String oldArguments = ""; //$NON-NLS-1$ String oldBuildLog = ""; //$NON-NLS-1$ if (config != null) { oldExecutable = config.getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, ""); //$NON-NLS-1$ oldArguments = config.getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS, ""); //$NON-NLS-1$ oldBuildLog = config.getAttribute(ICDTStandaloneDebugLaunchConstants.BUILD_LOG_LOCATION, ""); //$NON-NLS-1$ } final NewExecutableInfo info = new NewExecutableInfo("", "", "", ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ $NON-NLS-2$ $NON-NLS-3$ final IStatus errorStatus = new Status(IStatus.WARNING, Activator.PLUGIN_ID, 0, Messages.GdbDebugNewExecutableCommand_Binary_file_does_not_exist, null); final String executablePath = oldExecutable; final String executableArgs = oldArguments; final String buildLogPath = oldBuildLog; // Bring up New Executable dialog with values from // the last launch. Display.getDefault().syncExec(new Runnable() { @Override public void run() { NewExecutableDialog dialog = new NewExecutableDialog(getWindowConfigurer().getWindow().getShell(), 0, executablePath, buildLogPath, executableArgs); dialog.setBlockOnOpen(true); if (dialog.open() == IDialogConstants.OK_ID) { NewExecutableInfo info2 = dialog.getExecutableInfo(); info.setHostPath(info2.getHostPath()); info.setArguments(info2.getArguments()); info.setBuildLog(info2.getBuildLog()); } else { ErrorDialog.openError(null, Messages.DebuggerInitializingProblem, null, errorStatus, IStatus.ERROR | IStatus.WARNING); } } }); // Check and see if we failed above and if so, quit if (info.getHostPath().isEmpty()) { monitor.done(); // throw internal exception which will be caught below throw new StartupException(errorStatus.getMessage()); } executable = info.getHostPath(); arguments = info.getArguments(); buildLog = info.getBuildLog(); // If no last configuration or user has changed // the executable, we need to create a new configuration // and remove artifacts from the old one. if (config == null || !executable.equals(oldExecutable)) config = DebugExecutable.importAndCreateLaunchConfig(monitor, executable, buildLog, arguments, true); ILaunchConfigurationWorkingCopy wc = config.getWorkingCopy(); wc.setAttribute(ICDTStandaloneDebugLaunchConstants.BUILD_LOG_LOCATION, buildLog); if (arguments != null) wc.setAttribute( ICDTLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS, arguments); config = wc.doSave(); monitor.worked(7); } if (config != null) { final JobContainer LaunchJobs = new JobContainer(); Job.getJobManager().addJobChangeListener(new JobChangeAdapter() { @Override public void scheduled(IJobChangeEvent event) { Job job = event.getJob(); if (job.getName().contains(config.getName())) LaunchJobs.setLaunchJob(job); } @Override public void done(IJobChangeEvent event) { } }); monitor.subTask(Messages.LaunchingConfig); Display.getDefault().syncExec(new Runnable() { @Override public void run() { DebugUITools.launch(config, ILaunchManager.DEBUG_MODE); } }); if (LaunchJobs.getLaunchJob() != null) { try { LaunchJobs.getLaunchJob().join(); } catch (InterruptedException e) { IStatus status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, 0, Messages.LaunchInterruptedError, e); ResourcesPlugin.getPlugin().getLog().log(status); } } } } catch (InterruptedException e) { throw e; // rethrow exception } catch (CoreException e) { e.printStackTrace(); } catch (StartupException e) { // do nothing..just quit } catch (Exception e) { e.printStackTrace(); } finally { monitor.done(); } } } @Override public void postWindowClose() { if (ResourcesPlugin.getWorkspace() != null) disconnectFromWorkspace(); super.postWindowClose(); } private void disconnectFromWorkspace() { // save the workspace final MultiStatus status = new MultiStatus( Activator.PLUGIN_ID, 1, Messages.ProblemSavingWorkbench, null); try { final ProgressMonitorDialog p = new ProgressMonitorDialog( null); IRunnableWithProgress runnable = new IRunnableWithProgress() { @Override public void run(IProgressMonitor monitor) { try { status.merge(ResourcesPlugin .getWorkspace().save(true, monitor)); } catch (CoreException e) { status.merge(e.getStatus()); } } }; p.run(true, false, runnable); } catch (InvocationTargetException e) { status.merge(new Status(IStatus.ERROR, Activator.PLUGIN_ID, 1, Messages.InternalError, e.getTargetException())); } catch (InterruptedException e) { status.merge(new Status(IStatus.ERROR, Activator.PLUGIN_ID, 1, Messages.InternalError, e)); } ErrorDialog.openError(null, Messages.ProblemsSavingWorkspace, null, status, IStatus.ERROR | IStatus.WARNING); if (!status.isOK()) { ResourcesPlugin.getPlugin().getLog().log(status); } } }