/*******************************************************************************
* Copyright (c) 2010, 2011 Elliott Baron
* 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:
* Elliott Baron <ebaron@fedoraproject.org> - initial API and implementation
* Red Hat Inc. - rewrite to use RemoteConnection class
* Corey Ashford <cjashfor@us.ibm.com> - Modified for use with an RDT-based
* RemoteConnection class.
*******************************************************************************/
package org.eclipse.linuxtools.internal.valgrind.launch;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URISyntaxException;
import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileInfo;
import org.eclipse.core.filesystem.IFileStore;
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.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.linuxtools.internal.valgrind.ui.ValgrindUIPlugin;
import org.eclipse.linuxtools.internal.valgrind.ui.ValgrindViewPart;
import org.eclipse.linuxtools.profiling.launch.ConfigUtils;
import org.eclipse.linuxtools.profiling.launch.IRemoteCommandLauncher;
import org.eclipse.linuxtools.profiling.launch.IRemoteFileProxy;
import org.eclipse.linuxtools.profiling.launch.RemoteConnection;
import org.eclipse.linuxtools.profiling.launch.RemoteConnectionException;
import org.eclipse.linuxtools.tools.launch.core.factory.RuntimeProcessFactory;
import org.eclipse.linuxtools.valgrind.core.IValgrindMessage;
import org.eclipse.linuxtools.valgrind.launch.IValgrindOutputDirectoryProvider;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.Version;
/**
* @since 1.1
*/
public class ValgrindRemoteProxyLaunchDelegate extends ValgrindLaunchConfigurationDelegate {
private static final String VALGRIND_CMD = "valgrind"; //$NON-NLS-1$
private ConfigUtils configUtils;
public ValgrindRemoteProxyLaunchDelegate() {
super();
}
private static final String VERSION_OPT = "--version"; //$NON-NLS-1$
private String whichVersion(IProject project) {
String cmdArray[] = new String[2];
cmdArray[0] = VALGRIND_CMD;
cmdArray[1] = VERSION_OPT;
try {
Process p = RuntimeProcessFactory.getFactory().exec(cmdArray,
project);
try (BufferedReader stdout = new BufferedReader(
new InputStreamReader(p.getInputStream()))) {
return stdout.readLine();
}
} catch (IOException e) {
return null;
}
}
private static final String VERSION_PREFIX = "valgrind-"; //$NON-NLS-1$
private static final char VERSION_DELIMITER = '-';
private static final Version MIN_VER = ValgrindLaunchPlugin.VER_3_3_0;
private Version getValgrindVersion(IProject project) throws CoreException {
Version valgrindVersion;
String verString = whichVersion(project);
if (verString == null || verString.isEmpty()){
throw new CoreException(new Status(IStatus.ERROR, ValgrindLaunchPlugin.PLUGIN_ID, Messages.getString("ValgrindLaunchPlugin.Couldn't_determine_version"))); //$NON-NLS-1$
}
verString = verString.replace(VERSION_PREFIX, ""); //$NON-NLS-1$
if (verString.indexOf(VERSION_DELIMITER) > 0) {
verString = verString.substring(0, verString.indexOf(VERSION_DELIMITER));
}
if (!verString.isEmpty()) {
valgrindVersion = Version.parseVersion(verString);
} else {
throw new CoreException(new Status(IStatus.ERROR, ValgrindLaunchPlugin.PLUGIN_ID, Messages.getString("ValgrindLaunchPlugin.Couldn't_determine_version"))); //$NON-NLS-1$
}
// check for minimum supported version
if (valgrindVersion.compareTo(MIN_VER) < 0) {
throw new CoreException(new Status(IStatus.ERROR, ValgrindLaunchPlugin.PLUGIN_ID, NLS.bind(Messages.getString("ValgrindLaunchPlugin.Error_min_version"), valgrindVersion.toString(), MIN_VER.toString()))); //$NON-NLS-1$
}
return valgrindVersion;
}
@Override
public void launch(final ILaunchConfiguration config, String mode,
final ILaunch launch, IProgressMonitor m) throws CoreException {
if (m == null) {
m = new NullProgressMonitor();
}
// Clear process as we wait on it to be instantiated
process = null;
SubMonitor monitor = SubMonitor.convert(m,
Messages.getString("ValgrindRemoteLaunchDelegate.task_name"), 10); //$NON-NLS-1$
// check for cancellation
if (monitor.isCanceled()) {
return;
}
this.config = config;
this.launch = launch;
try {
// remove any output from previous run
ValgrindUIPlugin.getDefault().resetView();
// reset stored launch data
getPlugin().setCurrentLaunchConfiguration(null);
getPlugin().setCurrentLaunch(null);
this.configUtils = new ConfigUtils(config);
IProject project = configUtils.getProject();
ValgrindUIPlugin.getDefault().setProfiledProject(project);
URI exeURI = new URI(configUtils.getExecutablePath());
RemoteConnection exeRC = new RemoteConnection(exeURI);
monitor.worked(1);
String valgrindPathString = RuntimeProcessFactory.getFactory().whichCommand(VALGRIND_CMD, project);
IPath valgrindFullPath = Path.fromOSString(valgrindPathString);
boolean copyExecutable = configUtils.getCopyExecutable();
if (copyExecutable) {
URI copyExeURI = new URI(configUtils.getCopyFromExecutablePath());
RemoteConnection copyExeRC = new RemoteConnection(copyExeURI);
IRemoteFileProxy copyExeRFP = copyExeRC.getRmtFileProxy();
IFileStore copyExeFS = copyExeRFP.getResource(copyExeURI.getPath());
IRemoteFileProxy exeRFP = exeRC.getRmtFileProxy();
IFileStore exeFS = exeRFP.getResource(exeURI.getPath());
IFileInfo exeFI = exeFS.fetchInfo();
if (exeFI.isDirectory()) {
// Assume the user wants to copy the file to the given directory, using
// the same filename as the "copy from" executable.
IPath copyExePath = Path.fromOSString(copyExeURI.getPath());
IPath newExePath = Path.fromOSString(exeURI.getPath()).append(copyExePath.lastSegment());
// update the exeURI with the new path.
exeURI = new URI(exeURI.getScheme(), exeURI.getAuthority(), newExePath.toString(), exeURI.getQuery(), exeURI.getFragment());
exeFS = exeRFP.getResource(exeURI.getPath());
}
copyExeFS.copy(exeFS, EFS.OVERWRITE | EFS.SHALLOW, SubMonitor.convert(monitor, 1));
// Note: assume that we don't need to create a new exeRC since the
// scheme and authority remain the same between the original exeURI and the new one.
}
valgrindVersion = getValgrindVersion(project);
IPath remoteBinFile = Path.fromOSString(exeURI.getPath());
String configWorkingDir = configUtils.getWorkingDirectory();
IFileStore workingDir;
if(configWorkingDir == null){
// If no working directory was provided, use the directory containing the
// the executable as the working directory.
IPath workingDirPath = remoteBinFile.removeLastSegments(1);
IRemoteFileProxy workingDirRFP = exeRC.getRmtFileProxy();
workingDir = workingDirRFP.getResource(workingDirPath.toOSString());
} else {
URI workingDirURI = new URI(configUtils.getWorkingDirectory());
RemoteConnection workingDirRC = new RemoteConnection(workingDirURI);
IRemoteFileProxy workingDirRFP = workingDirRC.getRmtFileProxy();
workingDir = workingDirRFP.getResource(workingDirURI.getPath());
}
IPath remoteLogDir = Path.fromOSString("/tmp/"); //$NON-NLS-1$
outputPath = remoteLogDir.append("eclipse-valgrind-" + System.currentTimeMillis()); //$NON-NLS-1$
exeRC.createFolder(outputPath, SubMonitor.convert(monitor, 1));
// create/empty local output directory
IValgrindOutputDirectoryProvider provider = getPlugin().getOutputDirectoryProvider();
setOutputPath(config, provider.getOutputPath());
IPath localOutputDir = null;
try {
localOutputDir = provider.getOutputPath();
createDirectory(localOutputDir);
} catch (IOException e2) {
throw new CoreException(new Status(IStatus.ERROR, ValgrindLaunchPlugin.PLUGIN_ID, IStatus.OK,
"", e2)); //$NON-NLS-1$
}
// tool that was launched
toolID = getTool(config);
// ask tool extension for arguments
dynamicDelegate = getDynamicDelegate(toolID);
String[] valgrindArgs = getValgrindArgumentsArray(config);
String[] executableArgs = getProgramArgumentsArray(config);
String[] allArgs = new String[executableArgs.length + valgrindArgs.length + 2];
int idx = 0;
allArgs[idx++] = VALGRIND_CMD;
for (String valgrindArg : valgrindArgs) {
allArgs[idx++] = valgrindArg;
}
allArgs[idx++] = remoteBinFile.toOSString();
for (String executableArg : executableArgs) {
allArgs[idx++] = executableArg;
}
Process p = RuntimeProcessFactory.getFactory().exec(allArgs, new String[0], workingDir, project);
int state = p.waitFor();
if (state != IRemoteCommandLauncher.OK) {
abort(Messages.getString("ValgrindLaunchConfigurationDelegate.Launch_exited_status") + " " //$NON-NLS-1$ //$NON-NLS-2$
+ state + ". " + NLS.bind(Messages.getString("ValgrindRemoteProxyLaunchDelegate.see_reference"), "IRemoteCommandLauncher") //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ "\n", //$NON-NLS-1$
null, ICDTLaunchConfigurationConstants.ERR_INTERNAL_ERROR);
}
if (p.exitValue() != 0) {
String line = null;
StringBuilder valgrindOutSB = new StringBuilder();
BufferedReader valgrindOut = new BufferedReader(new InputStreamReader(p.getInputStream()));
while((line = valgrindOut.readLine()) != null ){
valgrindOutSB.append(line);
}
StringBuilder valgrindErrSB = new StringBuilder();
BufferedReader valgrindErr = new BufferedReader(new InputStreamReader(p.getErrorStream()));
while((line = valgrindErr.readLine()) != null ){
valgrindErrSB.append(line);
}
abort(NLS.bind("ValgrindRemoteProxyLaunchDelegate.Stdout", valgrindOutSB.toString()) + //$NON-NLS-1$
"\n" + NLS.bind("ValgrindRemoteProxyLaunchDelegate.Stderr", valgrindErrSB.toString()), //$NON-NLS-1$ //$NON-NLS-2$
null, ICDTLaunchConfigurationConstants.ERR_INTERNAL_ERROR);
}
// move remote log files to local directory
exeRC.download(outputPath, localOutputDir, SubMonitor.convert(monitor, 1));
// remove remote log dir and all files under it
exeRC.delete(outputPath, SubMonitor.convert(monitor, 1));
// store these for use by other classes
getPlugin().setCurrentLaunchConfiguration(config);
getPlugin().setCurrentLaunch(launch);
// parse Valgrind logs
IValgrindMessage[] messages = parseLogs(localOutputDir);
// create launch summary string to distinguish this launch
launchStr = createLaunchStr(valgrindFullPath);
// create view
ValgrindUIPlugin.getDefault().createView(launchStr, toolID);
// set log messages
ValgrindViewPart view = ValgrindUIPlugin.getDefault().getView();
view.setMessages(messages);
monitor.worked(1);
// pass off control to extender
dynamicDelegate.handleLaunch(config, launch, localOutputDir, monitor.newChild(2));
// initialize tool-specific part of view
dynamicDelegate.initializeView(view.getDynamicView(), launchStr, monitor.newChild(1));
// refresh view
ValgrindUIPlugin.getDefault().refreshView();
// show view
ValgrindUIPlugin.getDefault().showView();
monitor.worked(1);
} catch (URISyntaxException|IOException|RemoteConnectionException|InterruptedException e) {
abort(e.getLocalizedMessage(), null, ICDTLaunchConfigurationConstants.ERR_INTERNAL_ERROR);
} finally {
monitor.done();
m.done();
}
}
private String createLaunchStr(IPath valgrindPath) throws CoreException {
IProject project = configUtils.getProject();
URI projectURI = project.getLocationURI();
String host = projectURI.getHost();
// Host might be null since it's not needed for a well-formed URI. Try authority instead
if(host == null){
host = projectURI.getAuthority();
}
// If authority is also null, use a generic name
String location;
if(host == null){
location = "remote host"; //$NON-NLS-1$
} else {
location = projectURI.getScheme() + "://" + host; //$NON-NLS-1$
}
return config.getName()
+ " [" + getPlugin().getToolName(toolID) + "]" + " " + valgrindPath.toString() + " on " + location; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
}
@Override
protected String getPluginID() {
return ValgrindLaunchPlugin.PLUGIN_ID;
}
}