/* * #%~ * org.overture.ide.debug * %% * Copyright (C) 2008 - 2014 Overture * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-3.0.html>. * #~% */ package org.overture.ide.debug.core.launching; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Vector; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.FileLocator; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; 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.IProcess; import org.eclipse.debug.core.model.LaunchConfigurationDelegate; import org.osgi.framework.Bundle; import org.overture.ide.core.resources.IVdmProject; import org.overture.ide.core.resources.IVdmSourceUnit; import org.overture.ide.debug.core.IDbgpService; import org.overture.ide.debug.core.IDebugConstants; import org.overture.ide.debug.core.IDebugPreferenceConstants; import org.overture.ide.debug.core.VdmDebugPlugin; import org.overture.ide.debug.core.model.internal.VdmDebugTarget; import org.overture.ide.debug.utils.VdmProjectClassPathCollector; import org.overture.ide.ui.utility.VdmTypeCheckerUi; import org.overture.util.Base64; /** * @see http://www.eclipse.org/articles/Article-Debugger/how-to.html * @author ari and kel */ @SuppressWarnings("javadoc") public class VdmLaunchConfigurationDelegate extends LaunchConfigurationDelegate { public static final String ORG_OVERTURE_IDE_PLUGINS_PROBRUNTIME = "org.overture.ide.plugins.probruntime.core"; static int sessionId = 0;; public void launch(ILaunchConfiguration configuration, String mode, ILaunch launch, IProgressMonitor monitor) throws CoreException { if (monitor == null) { monitor = new NullProgressMonitor(); } monitor.beginTask("Debugger launching", 4); if (monitor.isCanceled()) { return; } try { // set launch encoding to UTF-8. Mainly used to set console encoding. launch.setAttribute(DebugPlugin.ATTR_CONSOLE_ENCODING, "UTF-8"); List<String> commandList = initializeLaunch(launch, configuration, mode, monitor); final VdmDebugTarget target = (VdmDebugTarget) launch.getDebugTarget(); final DebugSessionAcceptor acceptor = new DebugSessionAcceptor(target, monitor); try { if (!useRemoteDebug(configuration)) { target.setProcess(launchExternalProcess(launch, commandList, getVdmProject(configuration), configuration)); } monitor.worked(1); // Waiting for debugging engine to connect waitDebuggerConnected(launch, acceptor); } finally { acceptor.disposeStatusHandler(); } } catch (CoreException e) { launch.terminate(); throw e; } finally { monitor.done(); } } /** * Waiting debugging process to connect to current launch * * @param launch * launch to connect to * @param acceptor * @param monitor * progress monitor * @throws CoreException * if debuggingProcess terminated, monitor is canceled or // * timeout */ protected void waitDebuggerConnected(ILaunch launch, DebugSessionAcceptor acceptor) throws CoreException { int timeout = VdmDebugPlugin.getDefault().getConnectionTimeout(); if (!acceptor.waitConnection(timeout)) { launch.terminate(); return; } if (!acceptor.waitInitialized(60 * 60 * 1000)) { launch.terminate(); abort("errDebuggingEngineNotInitialized", null); } } /** * generate commandline argments for the debugger and create the debug target * @param launch * @param configuration * @param mode * @param monitor * @return * @throws CoreException */ private List<String> initializeLaunch(ILaunch launch, ILaunchConfiguration configuration, String mode, IProgressMonitor monitor) throws CoreException { List<String> commandList = null; Integer debugSessionId = Integer.valueOf(getSessionId()); if (useRemoteDebug(configuration)) { debugSessionId = 1; } commandList = new ArrayList<String>(); IVdmProject vdmProject = getVdmProject(configuration); Assert.isNotNull(vdmProject, " Project not found: " + configuration.getAttribute(IDebugConstants.VDM_LAUNCH_CONFIG_PROJECT, "")); if (vdmProject == null || !VdmTypeCheckerUi.typeCheck(vdmProject, monitor)) { abort("Cannot launch a project (" + vdmProject + ") with type errors, please check the problems view", null); } String charSet = getProject(configuration).getDefaultCharset(); commandList.add("-h"); commandList.add("localhost"); commandList.add("-p"); int port = VdmDebugPlugin.getDefault().getDbgpService().getPort(); // Hook for external tools to direct the debugger to listen on a specific port int overridePort = configuration.getAttribute(IDebugConstants.VDM_LAUNCH_CONFIG_OVERRIDE_PORT, IDebugPreferenceConstants.DBGP_AVAILABLE_PORT); if (overridePort != IDebugPreferenceConstants.DBGP_AVAILABLE_PORT) { port = VdmDebugPlugin.getDefault().getDbgpService(overridePort).getPort(); } commandList.add(Integer.valueOf(port).toString()); commandList.add("-k"); commandList.add(debugSessionId.toString()); commandList.add("-w"); commandList.add("-q"); commandList.add(vdmProject.getDialect().getArgstring()); commandList.add("-r"); commandList.add(vdmProject.getLanguageVersionName()); // set disable interpreter settings if (!configuration.getAttribute(IDebugConstants.VDM_LAUNCH_CONFIG_PRE_CHECKS, true))// vdmProject.hasPrechecks()) { commandList.add("-pre"); } if (!configuration.getAttribute(IDebugConstants.VDM_LAUNCH_CONFIG_POST_CHECKS, true))// vdmProject.hasPostchecks()) { commandList.add("-post"); } if (!configuration.getAttribute(IDebugConstants.VDM_LAUNCH_CONFIG_INV_CHECKS, true))// vdmProject.hasInvchecks()) { commandList.add("-inv"); } if (!configuration.getAttribute(IDebugConstants.VDM_LAUNCH_CONFIG_DTC_CHECKS, true))// vdmProject.hasDynamictypechecks()) { commandList.add("-dtc"); } if (!configuration.getAttribute(IDebugConstants.VDM_LAUNCH_CONFIG_MEASURE_CHECKS, true))// vdmProject.hasMeasurechecks()) { commandList.add("-measures"); } commandList.add("-c"); commandList.add(charSet); if (!isRemoteControllerEnabled(configuration)) { commandList.add("-e64"); commandList.add(getExpressionBase64(configuration, charSet)); String default64 = getDefaultBase64(configuration, charSet); if (default64.trim().length() > 0) { commandList.add("-default64"); commandList.add(getDefaultBase64(configuration, charSet)); } } else { // temp fix for commanline args of dbgreader commandList.add("-e64"); commandList.add(Base64.encode("A".getBytes()).toString()); } if (isRemoteControllerEnabled(configuration)) { commandList.add("-remote"); commandList.add(getRemoteControllerName(configuration)); } if (hasTrace(configuration)) { commandList.add("-t"); } commandList.add("-consoleName"); commandList.add("LaunchConfigurationExpression"); commandList.addAll(getExtendedCommands(vdmProject, configuration)); commandList.add("-baseDir"); commandList.add(getProject(configuration).getLocationURI().toASCIIString()); commandList.addAll(getSpecFiles(vdmProject)); if (useRemoteDebug(configuration)) { System.out.println("Debugger Arguments:\n" + getArgumentString(commandList)); } commandList.add(0, "java"); commandList.add(1, IDebugConstants.DEBUG_ENGINE_CLASS); if (configuration.getAttribute(IDebugConstants.VDM_LAUNCH_CONFIG_SHOW_VM_SETTINGS, false)) { commandList.addAll(1, Arrays.asList(new String[] { "-XshowSettings:all" })); } commandList.addAll(1, getVmArguments(configuration)); if (useRemoteDebug(configuration)) { System.out.println("Full Debugger Arguments:\n" + getArgumentString(commandList)); } VdmDebugTarget target = null; // Debug mode if (mode.equals(ILaunchManager.DEBUG_MODE)) { IDbgpService service = VdmDebugPlugin.getDefault().getDbgpService(); if (!service.available()) { abort("Could not create DBGP Service", null); } DebugPlugin.getDefault().getBreakpointManager().setEnabled(true); target = new VdmDebugTarget(IDebugConstants.ID_VDM_DEBUG_MODEL, service, debugSessionId.toString(), launch, null); target.setVdmProject(vdmProject); launch.addDebugTarget(target); target.toggleClassVariables(true); target.toggleGlobalVariables(true); target.toggleLocalVariables(true); } // Run mode else if (mode.equals(ILaunchManager.RUN_MODE)) { IDbgpService service = VdmDebugPlugin.getDefault().getDbgpService(); if (!service.available()) { abort("Could not create DBGP Service", null); } DebugPlugin.getDefault().getBreakpointManager().setEnabled(false); target = new VdmDebugTarget(IDebugConstants.ID_VDM_DEBUG_MODEL, service, debugSessionId.toString(), launch, null); target.setVdmProject(vdmProject); launch.addDebugTarget(target); target.toggleClassVariables(true); target.toggleGlobalVariables(true); target.toggleLocalVariables(true); } return commandList; } protected String[] getDebugEngineBundleIds() { List<String> ids = new ArrayList<String>(Arrays.asList(IDebugConstants.DEBUG_ENGINE_BUNDLE_IDS)); if (VdmDebugPlugin.getDefault().getPreferenceStore().getBoolean(IDebugPreferenceConstants.PREF_DBGP_ENABLE_EXPERIMENTAL_MODELCHECKER)) { ids.add(ORG_OVERTURE_IDE_PLUGINS_PROBRUNTIME); } return ids.toArray(new String[] {}); } private File prepareCustomDebuggerProperties(IVdmProject project, ILaunchConfiguration configuration) throws CoreException { List<String> properties = new Vector<String>(); String propertyCfg = configuration.getAttribute(IDebugConstants.VDM_LAUNCH_CONFIG_CUSTOM_DEBUGGER_PROPERTIES, ""); for (String p : propertyCfg.split(";")) { properties.add(p); } return writePropertyFile(project, "vdmj.properties", properties); } /** * Create the custom overture.properties file loaded by the debugger * @param project * @param configuration a configuration or null * @return * @throws CoreException */ public static File prepareCustomOvertureProperties(IVdmProject project, ILaunchConfiguration configuration) throws CoreException { List<String> properties = new Vector<String>(); if (VdmDebugPlugin.getDefault().getPreferenceStore().getBoolean(IDebugPreferenceConstants.PREF_DBGP_ENABLE_EXPERIMENTAL_MODELCHECKER)) { properties.add(getProbHomeProperty()); } return writePropertyFile(project, "overture.properties", properties); } public static File writePropertyFile(IVdmProject project, String filename, List<String> properties) throws CoreException { try { File outputDir = project.getModelBuildPath().getOutput().getLocation().toFile(); if (outputDir.mkdirs()) { // ignore } File vdmjProperties = new File(outputDir, filename); PrintWriter out = null; try { FileWriter outFile = new FileWriter(vdmjProperties); out = new PrintWriter(outFile); for (String property : properties) { out.println(property); } return vdmjProperties; } catch (IOException e) { abort("Faild to create custom properties file while writing properties", e); } finally { out.close(); } } catch (CoreException e) { abort("Faild to create custom properties file", e); } return null; } private synchronized int getSessionId() { return sessionId++; } private Collection<? extends String> getVmArguments( ILaunchConfiguration configuration) throws CoreException { List<String> options = new Vector<String>(); String opt = configuration.getAttribute(IDebugConstants.VDM_LAUNCH_CONFIG_VM_MEMORY_OPTION, ""); if (opt.trim().length() != 0) { String[] opts = opt.split(" "); for (String o : opts) { o = o.trim(); if (o.startsWith("-")) { options.add(o); } } } return options; } /** * Obtains the prob bundled executable and returns the prob.home property set to that location * @return the prob.home property if available or empty string */ public static String getProbHomeProperty() { final Bundle bundle = Platform.getBundle(ORG_OVERTURE_IDE_PLUGINS_PROBRUNTIME); if (bundle != null) { URL buildInfoUrl = FileLocator.find(bundle, new Path("prob/build_info.txt"), null); try { if (buildInfoUrl != null) { URL buildInfofileUrl = FileLocator.toFileURL(buildInfoUrl); if (buildInfofileUrl != null) { File file = new File(buildInfofileUrl.getFile()); return "system."+"prob.home=" + file.getParentFile().getPath().replace('\\', '/'); } } } catch (IOException e) { } } return ""; } /** * Intended to be used when sub classing the delegate to add additional parameters to the launch of VDMJ * * @param project * the project launched * @param configuration * the launch configuration * @return a list of parameters to be added to the command line just before the files * @throws CoreException */ protected Collection<? extends String> getExtendedCommands( IVdmProject project, ILaunchConfiguration configuration) throws CoreException { return new Vector<String>(); } private String getRemoteControllerName(ILaunchConfiguration configuration) throws CoreException { return configuration.getAttribute(IDebugConstants.VDM_LAUNCH_CONFIG_REMOTE_CONTROL, ""); } private String getArgumentString(List<String> args) { StringBuffer executeString = new StringBuffer(); for (String string : args) { executeString.append(string); executeString.append(" "); } return executeString.toString().trim(); } private boolean useRemoteDebug(ILaunchConfiguration configuration) throws CoreException { return configuration.getAttribute(IDebugConstants.VDM_LAUNCH_CONFIG_REMOTE_DEBUG, false); } private boolean isRemoteControllerEnabled(ILaunchConfiguration configuration) throws CoreException { return configuration.getAttribute(IDebugConstants.VDM_LAUNCH_CONFIG_REMOTE_CONTROL, "").length() > 0; } private boolean hasTrace(ILaunchConfiguration configuration) throws CoreException { return configuration.getAttribute(IDebugConstants.VDM_LAUNCH_CONFIG_IS_TRACE, false); } protected File getOutputFolder(IVdmProject project, ILaunchConfiguration configuration) throws CoreException { File outputDir = project.getModelBuildPath().getOutput().getLocation().toFile();// new outputDir.mkdirs(); return outputDir; } private IProcess launchExternalProcess(ILaunch launch, List<String> commandList, IVdmProject project, ILaunchConfiguration configuration) throws CoreException { ProcessBuilder procBuilder = new ProcessBuilder(commandList); File vdmjPropertiesFile = prepareCustomDebuggerProperties(project, configuration); File overturePropertiesFile = prepareCustomOvertureProperties(project, configuration); String classpath = VdmProjectClassPathCollector.toCpEnvString(VdmProjectClassPathCollector.getClassPath(getProject(configuration), getDebugEngineBundleIds(), vdmjPropertiesFile,overturePropertiesFile)); Map<String, String> env = procBuilder.environment(); env.put("CLASSPATH", classpath); Process process = null; try { procBuilder.directory(getProject(configuration).getLocation().toFile()); if (!useRemoteDebug(launch.getLaunchConfiguration())) { process = procBuilder.start(); } else { System.out.println("CLASSPATH = " + classpath); process = Runtime.getRuntime().exec("java -version"); } } catch (IOException e) { abort("Could not launch debug process", e); } return DebugPlugin.newProcess(launch, process, "Overture debugger"); } private String getDefaultBase64(ILaunchConfiguration configuration, String charset) throws CoreException { String defaultModule; try { defaultModule = configuration.getAttribute(IDebugConstants.VDM_LAUNCH_CONFIG_DEFAULT, ""); return Base64.encode(defaultModule.getBytes(charset)).toString(); } catch (UnsupportedEncodingException e) { abort("Unsuported encoding used for expression", e); } return ""; } private String getExpressionBase64(ILaunchConfiguration configuration, String charset) throws CoreException { String expression; try { if (configuration.getAttribute(IDebugConstants.VDM_LAUNCH_CONFIG_CONSOLE_ENTRY, false)) { expression = "###CONSOLE###"; } else { expression = configuration.getAttribute(IDebugConstants.VDM_LAUNCH_CONFIG_EXPRESSION, ""); } return Base64.encode(expression.getBytes(charset)).toString(); } catch (UnsupportedEncodingException e) { abort("Unsuported encoding used for expression", e); } return ""; } /** * Throws an exception with a new status containing the given message and optional exception. * * @param message * error message * @param e * underlying exception * @throws CoreException */ private static void abort(String message, Throwable e) throws CoreException { throw new CoreException((IStatus) new Status(IStatus.ERROR, IDebugConstants.PLUGIN_ID, 0, message, e)); } private List<String> getSpecFiles(IVdmProject project) throws CoreException { List<String> files = new Vector<String>(); for (IVdmSourceUnit unit : project.getSpecFiles()) { files.add(unit.getSystemFile().toURI().toASCIIString()); } return files; } static public IVdmProject getVdmProject(ILaunchConfiguration configuration) throws CoreException { IProject project = getProject(configuration); if (project != null) { IVdmProject vdmProject = (IVdmProject) project.getAdapter(IVdmProject.class); return vdmProject; } return null; } static private IProject getProject(ILaunchConfiguration configuration) throws CoreException { return ResourcesPlugin.getWorkspace().getRoot().getProject(configuration.getAttribute(IDebugConstants.VDM_LAUNCH_CONFIG_PROJECT, "")); } }