/*******************************************************************************
* Copyright (c) 2004, 2008, 2009 Red Hat, Inc. and others
* 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:
* Kent Sebastian <ksebasti@redhat.com> - initial API and implementation
* Keith Seitz <keiths@redhat.com> - setup code in launch the method, initially
* written in the now-defunct OprofileSession class
* QNX Software Systems and others - the section of code marked in the launch
* method, and the exec method
*******************************************************************************/
package org.eclipse.linuxtools.internal.oprofile.launch.launching;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import org.eclipse.cdt.debug.core.CDebugUtils;
import org.eclipse.cdt.launch.AbstractCLaunchDelegate;
import org.eclipse.core.filesystem.EFS;
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.Status;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.linuxtools.internal.oprofile.core.Oprofile;
import org.eclipse.linuxtools.internal.oprofile.core.Oprofile.OprofileProject;
import org.eclipse.linuxtools.internal.oprofile.core.OprofileCorePlugin;
import org.eclipse.linuxtools.internal.oprofile.core.daemon.OprofileDaemonEvent;
import org.eclipse.linuxtools.internal.oprofile.launch.OprofileLaunchMessages;
import org.eclipse.linuxtools.internal.oprofile.launch.OprofileLaunchPlugin;
import org.eclipse.linuxtools.internal.oprofile.launch.configuration.LaunchOptions;
import org.eclipse.linuxtools.internal.oprofile.launch.configuration.OprofileCounter;
import org.eclipse.linuxtools.internal.oprofile.ui.OprofileUiPlugin;
import org.eclipse.linuxtools.internal.oprofile.ui.view.OcountView;
import org.eclipse.linuxtools.internal.oprofile.ui.view.OprofileView;
import org.eclipse.linuxtools.profiling.launch.IRemoteFileProxy;
import org.eclipse.linuxtools.profiling.launch.RemoteProxyManager;
import org.eclipse.linuxtools.tools.launch.core.factory.RuntimeProcessFactory;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
public abstract class AbstractOprofileLaunchConfigurationDelegate extends AbstractCLaunchDelegate {
protected ILaunchConfiguration config;
private static final String OPROFILE_DATA = "oprofile_data"; //$NON-NLS-1$
private static final String OCOUNT_DATA = "ocount_data"; //$NON-NLS-1$
private static final String SESSION_DIR = "--session-dir="; //$NON-NLS-1$
private static final String OUTPUT_FILE = "--output-file="; //$NON-NLS-1$
private static final String EVENTS = "--events="; //$NON-NLS-1$
private static final String APPEND = "--append"; //$NON-NLS-1$
private static final String OPD_SETUP_EVENT_SEPARATOR = ":"; //$NON-NLS-1$
private static final String OPD_SETUP_EVENT_TRUE = "1"; //$NON-NLS-1$
private static final String OPD_SETUP_EVENT_FALSE = "0"; //$NON-NLS-1$
@Override
public void launch(ILaunchConfiguration config, String mode, ILaunch launch, IProgressMonitor monitor) throws CoreException {
this.config = config;
Oprofile.OprofileProject.setProject(getProject());
LaunchOptions options = new LaunchOptions(); //default options created in the constructor
options.loadConfiguration(config);
IPath exePath = getExePath(config);
options.setBinaryImage(exePath.toOSString());
Oprofile.OprofileProject.setProfilingBinary(options.getOprofileComboText());
/*
* Parameters needed for the application under profiling
*/
String appArgs[] = getProgramArgumentsArray(config);
String appEnv[] = getEnvironment(config);
//if daemonEvents null or zero size, the default event will be used
OprofileDaemonEvent[] daemonEvents = null;
ArrayList<OprofileDaemonEvent> events = new ArrayList<>();
if (!config.getAttribute(OprofileLaunchPlugin.ATTR_USE_DEFAULT_EVENT, false)) {
//get the events to profile from the counters
OprofileCounter[] counters = oprofileCounters(config);
for (int i = 0; i < counters.length; ++i) {
if (counters[i].getEnabled()) {
OprofileDaemonEvent[] counterEvents = counters[i].getDaemonEvents();
events.addAll(Arrays.asList(counterEvents));
}
}
daemonEvents = new OprofileDaemonEvent[events.size()];
events.toArray(daemonEvents);
}
/*
* this code written by QNX Software Systems and others and was
* originally in the CDT under LocalCDILaunchDelegate::RunLocalApplication
*/
if (!preExec(options, daemonEvents, launch)) {
return;
}
Process process = null;
// Executing operf with the default or specified events,
// outputing the profiling data to the project dir/OPROFILE_DATA
if (OprofileProject.getProfilingBinary().equals(OprofileProject.OPERF_BINARY)) {
String eventsString=null;
// Event spec: "EVENT:count:mask:profileKernel:profileUser"
StringBuilder spec = new StringBuilder();
spec.append(EVENTS);
boolean isCommaAllowed = false;
for (int i=0;i<events.size();i++) {
OprofileDaemonEvent event = events.get(i);
if(isCommaAllowed) {
spec.append(',');
}
spec.append(event.getEvent().getText());
spec.append(OPD_SETUP_EVENT_SEPARATOR);
spec.append(event.getResetCount());
spec.append(OPD_SETUP_EVENT_SEPARATOR);
spec.append(event.getEvent().getUnitMask().getMaskValue());
spec.append(OPD_SETUP_EVENT_SEPARATOR);
spec.append((event.getProfileKernel() ? OPD_SETUP_EVENT_TRUE : OPD_SETUP_EVENT_FALSE));
spec.append(OPD_SETUP_EVENT_SEPARATOR);
spec.append((event.getProfileUser() ? OPD_SETUP_EVENT_TRUE : OPD_SETUP_EVENT_FALSE));
isCommaAllowed = true;
}
eventsString = spec.toString();
ArrayList<String> argArray = new ArrayList<>(Arrays.asList(appArgs));
// Use remote file proxy to determine data folder since the project may be either local or remote
IRemoteFileProxy proxy = RemoteProxyManager.getInstance().getFileProxy(OprofileProject.getProject());
IFileStore dataFolder = proxy.getResource(oprofileWorkingDirURI(config).getPath() + IPath.SEPARATOR + OPROFILE_DATA);
if(! dataFolder.fetchInfo().exists()) {
dataFolder.mkdir(EFS.SHALLOW, null);
}
argArray.add(0, exePath.toOSString());
if (events.size()>0) {
argArray.add(0,eventsString);
}
argArray.add(0, SESSION_DIR + oprofileWorkingDirURI(config).getPath() + IPath.SEPARATOR + OPROFILE_DATA);
argArray.add(0, OprofileProject.OPERF_BINARY);
boolean appended = false;
for(int i = 0; i < options.getExecutionsNumber(); i++){
/*
* If profiling multiple times,
* append oprofile results from 2nd execution on.
*/
if (!appended && i!=0) {
argArray.add(1, APPEND);
appended = true;
}
String[] arguments = new String[argArray.size()];
arguments = argArray.toArray(arguments);
try {
process = RuntimeProcessFactory.getFactory().exec(arguments, appEnv , OprofileProject.getProject());
} catch (IOException e1) {
if (process != null)
process.destroy();
Status status = new Status(IStatus.ERROR, OprofileLaunchPlugin.PLUGIN_ID, OprofileLaunchMessages.getString("oprofilelaunch.error.interrupted_error.status_message")); //$NON-NLS-1$
throw new CoreException(status);
}
DebugPlugin.newProcess( launch, process, renderProcessLabel( exePath.toOSString() ) );
try{
process.waitFor();
} catch (InterruptedException e){
if (process != null)
process.destroy();
Status status = new Status(IStatus.ERROR, OprofileLaunchPlugin.PLUGIN_ID, OprofileLaunchMessages.getString("oprofilelaunch.error.interrupted_error.status_message")); //$NON-NLS-1$
throw new CoreException(status);
}
}
}
// Executing ocount with the default or specified events,
// outputing the profiling data to the project dir/OPROFILE_DATA
if (OprofileProject.getProfilingBinary().equals(OprofileProject.OCOUNT_BINARY)) {
String eventsString=null;
// Event spec: "EVENT:count:mask:profileKernel:profileUser"
StringBuilder spec = new StringBuilder();
spec.append(EVENTS);
boolean isCommaAllowed = false;
for (int i=0;i<events.size();i++) {
OprofileDaemonEvent event = events.get(i);
if(isCommaAllowed) {
spec.append(',');
}
spec.append(event.getEvent().getText());
spec.append(OPD_SETUP_EVENT_SEPARATOR);
spec.append(event.getResetCount());
spec.append(OPD_SETUP_EVENT_SEPARATOR);
spec.append(event.getEvent().getUnitMask().getMaskValue());
spec.append(OPD_SETUP_EVENT_SEPARATOR);
spec.append((event.getProfileKernel() ? OPD_SETUP_EVENT_TRUE : OPD_SETUP_EVENT_FALSE));
spec.append(OPD_SETUP_EVENT_SEPARATOR);
spec.append((event.getProfileUser() ? OPD_SETUP_EVENT_TRUE : OPD_SETUP_EVENT_FALSE));
isCommaAllowed = true;
}
eventsString = spec.toString();
ArrayList<String> argArray = new ArrayList<>(Arrays.asList(appArgs));
argArray.add(0, exePath.toOSString());
if (events.size()>0) {
argArray.add(0,eventsString);
}
argArray.add(0, OUTPUT_FILE + oprofileWorkingDirURI(config).getPath() + IPath.SEPARATOR + OCOUNT_DATA);
argArray.add(0, OprofileProject.OCOUNT_BINARY);
String[] arguments = new String[argArray.size()];
arguments = argArray.toArray(arguments);
try {
process = RuntimeProcessFactory.getFactory().exec(arguments, OprofileProject.getProject());
} catch (IOException e1) {
process.destroy();
Status status = new Status(IStatus.ERROR, OprofileLaunchPlugin.PLUGIN_ID, OprofileLaunchMessages.getString("oprofilelaunch.error.interrupted_error.status_message")); //$NON-NLS-1$
throw new CoreException(status);
}
DebugPlugin.newProcess( launch, process, renderProcessLabel( exePath.toOSString() ) );
try{
process.waitFor();
// Put the OCount data in a separate view
StringBuffer buffer = new StringBuffer();
// Use remote file proxy to operate resources since the project may be either local or remote
IRemoteFileProxy proxy = RemoteProxyManager.getInstance().getFileProxy(OprofileProject.getProject());
IFileStore ocountDataStore = proxy.getResource(oprofileWorkingDirURI(config).getPath() +
IPath.SEPARATOR + OCOUNT_DATA);
try (
InputStream is = ocountDataStore.openInputStream(EFS.NONE, monitor);
BufferedReader reader = new BufferedReader(new InputStreamReader(is))
) {
String s = reader.readLine();
String sep_char = ""; //$NON-NLS-1$
while (s != null) {
buffer.append(s);
buffer.append(sep_char);
sep_char = "\n"; //$NON-NLS-1$
s = reader.readLine();
}
// Open the OCount View and display output from ocount
final String text = buffer.toString();
Display.getDefault().syncExec(() -> refreshOcountView(text));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (InterruptedException e){
process.destroy();
Status status = new Status(IStatus.ERROR, OprofileLaunchPlugin.PLUGIN_ID, OprofileLaunchMessages.getString("oprofilelaunch.error.interrupted_error.status_message")); //$NON-NLS-1$
throw new CoreException(status);
}
return;
}
postExec(options, daemonEvents, process);
}
protected abstract boolean preExec(LaunchOptions options, OprofileDaemonEvent[] daemonEvents, ILaunch launch);
protected abstract void postExec(LaunchOptions options, OprofileDaemonEvent[] daemonEvents, Process process);
@Override
protected String getPluginID() {
return OprofileLaunchPlugin.PLUGIN_ID;
}
//Helper function to refresh the oprofile view. Opens and focuses the view
// if it isn't already.
protected void refreshOprofileView() {
OprofileView view = OprofileUiPlugin.getDefault().getOprofileView();
if (view != null) {
view.refreshView();
} else {
try {
PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().showView(OprofileUiPlugin.ID_OPROFILE_VIEW);
} catch (PartInitException e2) {
e2.printStackTrace();
}
OprofileUiPlugin.getDefault().getOprofileView().refreshView();
}
}
//Helper function to refresh the ocount view. Opens and focuses the view
// if it isn't already.
protected void refreshOcountView(String text) {
OcountView view = OprofileUiPlugin.getDefault().getOcountView();
if (view == null) {
try {
PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().showView(OprofileUiPlugin.ID_OCOUNT_VIEW);
} catch (PartInitException e2) {
e2.printStackTrace();
return;
}
view = OprofileUiPlugin.getDefault().getOcountView();
}
view.setText(text);
view.refreshView();
}
/* all these functions exist to be overridden by the test class in order to allow launch testing */
protected IProject oprofileProject(){
return Oprofile.OprofileProject.getProject();
}
/**
* Return the URI of the current working directory from the current
* project's file proxy.
*
* @return URI URI of the working directory.
* @throws CoreException
*/
protected URI oprofileWorkingDirURI(ILaunchConfiguration config) throws CoreException{
File workingDirectory = this.getWorkingDirectory(config);
if(workingDirectory == null){
return getProject().getLocationURI();
} else {
URI uri = null;
try {
uri = new URI(workingDirectory.getAbsolutePath());
} catch (URISyntaxException e) {
//Since working directory paths are verified by the launch tab, this exception should never be thrown
Status status = new Status(IStatus.ERROR, OprofileCorePlugin.getId(),
OprofileLaunchMessages.getString("oprofilelaunch.error.invalidworkingdir.status_message")); //$NON-NLS-1$
throw new CoreException(status);
}
return uri;
}
}
protected OprofileCounter[] oprofileCounters(ILaunchConfiguration config){
return OprofileCounter.getCounters(config);
}
/**
* Runs opcontrol --help. Returns true if there was any output, false
* otherwise. Return value can be used to tell if the user successfully
* entered a password.
* @return true if opcontrol --help was run correctly. False otherwise
*/
protected boolean oprofileStatus() {
if (OprofileProject.getProfilingBinary().equals(OprofileProject.OPERF_BINARY)) {
try {
Process p = RuntimeProcessFactory.getFactory().exec(new String[] { "operf", "--version" }, //$NON-NLS-1$ //$NON-NLS-2$
OprofileProject.getProject());
return (p != null);
} catch (IOException e) {
return false;
}
} else {
try {
Process p = RuntimeProcessFactory.getFactory().exec(new String[] { "ocount", "--version" }, //$NON-NLS-1$ //$NON-NLS-2$
OprofileProject.getProject());
return (p != null);
} catch (IOException e) {
return false;
}
}
}
protected IProject getProject(){
try{
return CDebugUtils.verifyCProject(config).getProject();
} catch (CoreException e) {
e.printStackTrace();
}
return null;
}
/**
*
* @param config
* @return
* @throws CoreException
* @since 1.1
*/
protected IPath getExePath(ILaunchConfiguration config) throws CoreException{
return CDebugUtils.verifyProgramPath( config );
}
}