blob: bf2f19d2074c2bc1f7499b48f61293ac68ba0775 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2018 Red Hat, Inc.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat - initial API and implementation
*******************************************************************************/
package org.eclipse.linuxtools.internal.callgraph.launch;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import org.eclipse.cdt.launch.AbstractCLaunchDelegate;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.IStreamListener;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.debug.core.model.IStreamMonitor;
import org.eclipse.linuxtools.internal.callgraph.core.DocWriter;
import org.eclipse.linuxtools.internal.callgraph.core.Helper;
import org.eclipse.linuxtools.internal.callgraph.core.LaunchConfigurationConstants;
import org.eclipse.linuxtools.internal.callgraph.core.PluginConstants;
import org.eclipse.linuxtools.internal.callgraph.core.SystemTapCommandGenerator;
import org.eclipse.linuxtools.internal.callgraph.core.SystemTapErrorHandler;
import org.eclipse.linuxtools.internal.callgraph.core.SystemTapParser;
import org.eclipse.linuxtools.internal.callgraph.core.SystemTapUIErrorMessages;
import org.eclipse.linuxtools.tools.launch.core.factory.CdtSpawnerProcessFactory;
/**
* Delegate for Stap scripts. The Delegate generates part of the command string
* and schedules a job to finish generation of the command and execute.
*
*/
public class SystemTapLaunchConfigurationDelegate extends AbstractCLaunchDelegate {
private static final String [] escapableChars = new String [] {"(", ")", " "}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
private static final String TEMP_ERROR_OUTPUT =
PluginConstants.getDefaultOutput() + "stapTempError.error"; //$NON-NLS-1$
private String cmd;
private File temporaryScript = null;
private String arguments = ""; //$NON-NLS-1$
private String scriptPath = ""; //$NON-NLS-1$
private String binaryPath = ""; //$NON-NLS-1$
private String outputPath = ""; //$NON-NLS-1$
private boolean needsBinary = false; // Set to false if we want to use SystemTap
private boolean needsArguments = false;
private String binaryArguments = ""; //$NON-NLS-1$
private String partialCommand = ""; //$NON-NLS-1$
private String stap = ""; //$NON-NLS-1$
@Override
protected String getPluginID() {
return null;
}
/**
* Sets strings to blank, booleans to false and everything else to null
*/
private void initialize() {
temporaryScript = null;
arguments = ""; //$NON-NLS-1$
scriptPath = ""; //$NON-NLS-1$
binaryPath = ""; //$NON-NLS-1$
outputPath = ""; //$NON-NLS-1$
needsBinary = false; // Set to false if we want to use SystemTap
needsArguments = false;
binaryArguments = ""; //$NON-NLS-1$
}
@Override
public void launch(ILaunchConfiguration config, String mode,
ILaunch launch, IProgressMonitor m) throws CoreException {
if (m == null) {
m = new NullProgressMonitor();
}
SubMonitor monitor = SubMonitor.convert(m,
"SystemTap runtime monitor", 5); //$NON-NLS-1$
initialize();
// check for cancellation
if (monitor.isCanceled()) {
return;
}
/*
* Set variables
*/
if (!config.getAttribute(LaunchConfigurationConstants.ARGUMENTS,
LaunchConfigurationConstants.DEFAULT_ARGUMENTS).equals(
LaunchConfigurationConstants.DEFAULT_ARGUMENTS)) {
arguments = config.getAttribute(
LaunchConfigurationConstants.ARGUMENTS,
LaunchConfigurationConstants.DEFAULT_ARGUMENTS);
needsArguments = true;
}
if (!config.getAttribute(LaunchConfigurationConstants.BINARY_PATH,
LaunchConfigurationConstants.DEFAULT_BINARY_PATH).equals(
LaunchConfigurationConstants.DEFAULT_BINARY_PATH)) {
binaryPath = config.getAttribute(
LaunchConfigurationConstants.BINARY_PATH,
LaunchConfigurationConstants.DEFAULT_BINARY_PATH);
needsBinary = true;
}
if (!config.getAttribute(LaunchConfigurationConstants.BINARY_ARGUMENTS,
LaunchConfigurationConstants.DEFAULT_BINARY_ARGUMENTS).equals(
LaunchConfigurationConstants.DEFAULT_BINARY_ARGUMENTS)) {
binaryArguments = config.getAttribute(
LaunchConfigurationConstants.BINARY_ARGUMENTS,
LaunchConfigurationConstants.DEFAULT_BINARY_ARGUMENTS);
}
if (!config.getAttribute(LaunchConfigurationConstants.SCRIPT_PATH,
LaunchConfigurationConstants.DEFAULT_SCRIPT_PATH).equals(
LaunchConfigurationConstants.DEFAULT_SCRIPT_PATH)) {
scriptPath = config.getAttribute(
LaunchConfigurationConstants.SCRIPT_PATH,
LaunchConfigurationConstants.DEFAULT_SCRIPT_PATH);
}
// Generate script if needed
if (config.getAttribute(LaunchConfigurationConstants.NEED_TO_GENERATE,
LaunchConfigurationConstants.DEFAULT_NEED_TO_GENERATE)) {
temporaryScript = new File(scriptPath);
temporaryScript.delete();
try {
temporaryScript.createNewFile();
try (FileWriter fstream = new FileWriter(temporaryScript);
BufferedWriter out = new BufferedWriter(fstream)) {
out.write(config
.getAttribute(
LaunchConfigurationConstants.GENERATED_SCRIPT,
LaunchConfigurationConstants.DEFAULT_GENERATED_SCRIPT));
}
} catch (IOException e1) {
e1.printStackTrace();
}
}
stap = config.getAttribute(LaunchConfigurationConstants.COMMAND,
PluginConstants.STAP_PATH);
/**
* Generate partial command
*/
partialCommand = ConfigurationOptionsSetter.setOptions(config);
outputPath = config.getAttribute(
LaunchConfigurationConstants.OUTPUT_PATH,
PluginConstants.getDefaultOutput());
boolean fileExists = true;
try {
//Make sure the output file exists
File tempFile = new File(outputPath);
tempFile.delete();
fileExists = tempFile.createNewFile();
} catch (IOException e1) {
fileExists = false;
}
// check for cancellation
if ( !fileExists || monitor.isCanceled() ) {
SystemTapUIErrorMessages mess = new SystemTapUIErrorMessages(Messages.getString("SystemTapLaunchConfigurationDelegate.0"), //$NON-NLS-1$
Messages.getString("SystemTapLaunchConfigurationDelegate.1"), Messages.getString("SystemTapLaunchConfigurationDelegate.2") + outputPath + //$NON-NLS-1$ //$NON-NLS-2$
Messages.getString("SystemTapLaunchConfigurationDelegate.3")); //$NON-NLS-1$
mess.schedule();
return;
}
finishLaunch(launch, config, m);
}
/**
* Returns the current SystemTap command, or returns an error message.
* @return
*/
public String getCommand() {
if (!cmd.isEmpty()) {
return cmd;
} else {
return Messages.getString("SystemTapLaunchConfigurationDelegate.NoCommand"); //$NON-NLS-1$
}
}
private void finishLaunch(ILaunch launch, ILaunchConfiguration config,
IProgressMonitor monitor) {
try {
// Check for cancellation
if (monitor.isCanceled() || launch == null) {
return;
}
monitor.worked(1);
// set the default source locator if required
setDefaultSourceLocator(launch, config);
/*
* Fetch a parser
*/
String parserClass = config.getAttribute(LaunchConfigurationConstants.PARSER_CLASS,
LaunchConfigurationConstants.DEFAULT_PARSER_CLASS);
IExtensionRegistry reg = Platform.getExtensionRegistry();
IConfigurationElement[] extensions = reg
.getConfigurationElementsFor(PluginConstants.PARSER_RESOURCE,
PluginConstants.PARSER_NAME,
parserClass);
if (extensions == null || extensions.length < 1) {
SystemTapUIErrorMessages mess = new SystemTapUIErrorMessages(Messages.getString("SystemTapLaunchConfigurationDelegate.InvalidParser1"), //$NON-NLS-1$
Messages.getString("SystemTapLaunchConfigurationDelegate.InvalidParser1"), //$NON-NLS-1$
Messages.getString("SystemTapLaunchConfigurationDelegate.InvalidParser2") + //$NON-NLS-1$
Messages.getString("SystemTapLaunchConfigurationDelegate.InvalidParser3") + parserClass); //$NON-NLS-1$
mess.schedule();
return;
}
IConfigurationElement element = extensions[0];
SystemTapParser parser =
(SystemTapParser) element.createExecutableExtension(PluginConstants.ATTR_CLASS);
//Set parser options
parser.setViewID(config.getAttribute(LaunchConfigurationConstants.VIEW_CLASS,
LaunchConfigurationConstants.VIEW_CLASS));
parser.setSourcePath(outputPath);
parser.setMonitor(SubMonitor.convert(monitor));
parser.setDone(false);
parser.setSecondaryID(config.getAttribute(LaunchConfigurationConstants.SECONDARY_VIEW_ID,
LaunchConfigurationConstants.DEFAULT_SECONDARY_VIEW_ID));
parser.setKillButtonEnabled(true);
monitor.worked(1);
/*
* Launch
*/
File workDir = getWorkingDirectory(config);
if (workDir == null) {
workDir = new File(System.getProperty("user.home", ".")); //$NON-NLS-1$ //$NON-NLS-2$
}
//Put command into a shell script
String cmd = generateCommand();
File script = File.createTempFile("org.eclipse.linuxtools.profiling.launch" + System.currentTimeMillis(), ".sh"); //$NON-NLS-1$ //$NON-NLS-2$
String data = "#!/bin/sh\nexec " + cmd; //$NON-NLS-1$
try (FileOutputStream out = new FileOutputStream(script)){
out.write(data.getBytes());
}
String [] commandArray = new String [] {"sh", script.getAbsolutePath()}; //$NON-NLS-1$
Process subProcess = CdtSpawnerProcessFactory.getFactory().exec(commandArray, getEnvironment(config),
workDir, true);
IProcess process = DebugPlugin.newProcess(launch, subProcess,
renderProcessLabel(commandArray[0]));
// set the command line used
process.setAttribute(IProcess.ATTR_CMDLINE,cmd);
monitor.worked(1);
StreamListener s = new StreamListener();
process.getStreamsProxy().getErrorStreamMonitor().addListener(s);
while (!process.isTerminated()) {
Thread.sleep(100);
if ((monitor != null && monitor.isCanceled()) || parser.isDone()) {
parser.cancelJob();
process.terminate();
return;
}
}
Thread.sleep(100);
s.close();
parser.setKillButtonEnabled(false);
if (process.getExitValue() != 0) {
parser.cancelJob();
//exit code for command not found
if (process.getExitValue() == 127){
SystemTapUIErrorMessages errorDialog = new SystemTapUIErrorMessages(
Messages.getString("SystemTapLaunchConfigurationDelegate.CallGraphGenericError"), //$NON-NLS-1$
Messages.getString("SystemTapLaunchConfigurationDelegate.CallGraphGenericError"), //$NON-NLS-1$
Messages.getString("SystemTapLaunchConfigurationDelegate.stapNotFound")); //$NON-NLS-1$
errorDialog.schedule();
}else{
SystemTapErrorHandler errorHandler = new SystemTapErrorHandler();
//Prepare stap information
errorHandler.appendToLog(config.getName() + Messages.getString("SystemTapLaunchConfigurationDelegate.stap_command") + cmd+ PluginConstants.NEW_LINE + PluginConstants.NEW_LINE);//$NON-NLS-1$
//Handle error from TEMP_ERROR_OUTPUT
errorHandler.handle(monitor, new FileReader(TEMP_ERROR_OUTPUT));
if ((monitor != null && monitor.isCanceled())) {
return;
}
errorHandler.finishHandling();
if (errorHandler.isErrorRecognized()) {
SystemTapUIErrorMessages errorDialog = new SystemTapUIErrorMessages(
Messages.getString("SystemTapLaunchConfigurationDelegate.CallGraphGenericError"), //$NON-NLS-1$
Messages.getString("SystemTapLaunchConfigurationDelegate.CallGraphGenericError"), //$NON-NLS-1$
errorHandler.getErrorMessage());
errorDialog.schedule();
}
}
return;
}
if (element.getAttribute(PluginConstants.ATTR_REALTIME).equals(PluginConstants.VAL_TRUE)) {
parser.setRealTime(true);
}
parser.schedule();
monitor.worked(1);
String message = generateErrorMessage(config.getName(), binaryArguments);
DocWriter dw = new DocWriter(Messages.getString("SystemTapLaunchConfigurationDelegate.DocWriterName"), //$NON-NLS-1$
(Helper.getConsoleByName(config.getName())), message);
dw.schedule();
} catch (IOException|InterruptedException|CoreException e) {
e.printStackTrace();
} finally {
monitor.done();
}
}
private String generateErrorMessage(String configName, String binaryCommand) {
String output = ""; //$NON-NLS-1$
if (binaryCommand == null || binaryCommand.length() < 0) {
output = PluginConstants.NEW_LINE +
PluginConstants.NEW_LINE + "-------------" + //$NON-NLS-1$
PluginConstants.NEW_LINE +
Messages.getString("SystemTapLaunchConfigurationDelegate.Relaunch10") //$NON-NLS-1$
+ configName + PluginConstants.NEW_LINE +
Messages.getString("SystemTapLaunchConfigurationDelegate.Relaunch8") + //$NON-NLS-1$
Messages.getString("SystemTapLaunchConfigurationDelegate.Relaunch9") + //$NON-NLS-1$
"configuration in Profile As --> Profile Configurations." + //$NON-NLS-1$
PluginConstants.NEW_LINE + PluginConstants.NEW_LINE;
} else {
output = PluginConstants.NEW_LINE
+ PluginConstants.NEW_LINE +"-------------" //$NON-NLS-1$
+ PluginConstants.NEW_LINE
+ Messages.getString("SystemTapLaunchConfigurationDelegate.EndMessage1") //$NON-NLS-1$
+ configName + PluginConstants.NEW_LINE +
Messages.getString("SystemTapLaunchConfigurationDelegate.EndMessage2") //$NON-NLS-1$
+ binaryCommand + PluginConstants.NEW_LINE +
Messages.getString("SystemTapLaunchConfigurationDelegate.EndMessage3") + //$NON-NLS-1$
Messages.getString("SystemTapLaunchConfigurationDelegate.EndMessage4") + //$NON-NLS-1$
Messages.getString("SystemTapLaunchConfigurationDelegate.EndMessage5") + //$NON-NLS-1$
PluginConstants.NEW_LINE + PluginConstants.NEW_LINE;
}
return output;
}
private static class StreamListener implements IStreamListener{
private int counter;
private BufferedWriter bw;
public StreamListener() throws IOException {
File file = new File(TEMP_ERROR_OUTPUT);
file.delete();
file.createNewFile();
bw = Helper.setBufferedWriter(TEMP_ERROR_OUTPUT);
counter = 0;
}
@Override
public void streamAppended(String text, IStreamMonitor monitor) {
try {
if (text.length() < 1) {
return;
}
counter++;
if (counter < PluginConstants.MAX_ERRORS) {
bw.append(text);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void close() throws IOException {
bw.close();
}
}
public String generateCommand() {
// Generate the command
cmd = SystemTapCommandGenerator.generateCommand(escapeSpecialCharacters(scriptPath), escapeSpecialCharacters(binaryPath),
partialCommand, needsBinary, needsArguments, escapeSpecialCharacters(arguments), binaryArguments, stap);
cmd += " >& " + escapeSpecialCharacters(outputPath); //$NON-NLS-1$
return cmd;
}
/**
* Escapes special characters in the target string
*
* @param script the script to be executed by the shell.
* @return the formatted string that will be executed.
*/
private String escapeSpecialCharacters(String str) {
// Modify script to catch escapable characters.
String res = str;
for (int i = 0; i < escapableChars.length; i++) {
res = res.replace(escapableChars[i], "\\" + escapableChars[i]); //$NON-NLS-1$
}
return res;
}
}