blob: f16ad1ee2d5ec6710f8425ab3254e025bf4551cd [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2018 Red Hat, Inc. and others.
*
* 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:
* 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
* Red Hat Inc. - modification of OProfileLaunchConfigurationDelegate to here
* Red Hat Inc. - fix #408543
*******************************************************************************/
package org.eclipse.linuxtools.internal.gprof.launch;
import java.io.File;
import org.eclipse.cdt.debug.core.CDebugUtils;
import org.eclipse.cdt.launch.AbstractCLaunchDelegate;
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.Path;
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.ILaunchesListener2;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.linuxtools.internal.gprof.view.GmonView;
import org.eclipse.linuxtools.profiling.launch.IRemoteCommandLauncher;
import org.eclipse.linuxtools.profiling.launch.IRemoteFileProxy;
import org.eclipse.linuxtools.profiling.launch.RemoteProxyManager;
import org.eclipse.linuxtools.profiling.ui.CProjectBuildHelpers;
import org.eclipse.linuxtools.profiling.ui.CProjectBuildHelpers.ProjectBuildType;
import org.eclipse.linuxtools.profiling.ui.MessageDialogSyncedRunnable;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PlatformUI;
public class GprofLaunchConfigurationDelegate extends AbstractCLaunchDelegate {
private ILaunchConfiguration config;
private IProject project;
/**
* Checks if neccessary flags are set in Managed Build/Autotools <br>
* If they are not, asks the user if he want's to have them addded. <br>
* If so, it enables the option, rebuilds the project and continues with launch </p>
*
* <p> Otherwise by this time the project is already build and it proceeds with launch of the plugin. </p>
*
*/
@Override
public void launch(ILaunchConfiguration config, String mode, ILaunch launch, IProgressMonitor monitor) throws CoreException {
//Save passed variables. useful later.
this.config = config;
this.project = getProject();
//Check pre-requisites, (this means check if the pg flag is set).
//If it is not and the user does not wish to have them set automatically,
//then cancle the launch process as there won't a gmon.out anyway.
if (! preRequisiteCheck()) {
return;
}
IPath exePath = getExePath(config);
/*
* this code written by QNX Software Systems and others and was
* originally in the CDT under LocalCDILaunchDelegate::RunLocalApplication
*/
//set up and launch the local c/c++ program
IRemoteCommandLauncher launcher = RemoteProxyManager.getInstance().getLauncher(getProject());
File workDir = getWorkingDirectory(config);
if (workDir == null) {
workDir = new File(System.getProperty("user.home", ".")); //$NON-NLS-1$ //$NON-NLS-2$
}
String arguments[] = getProgramArgumentsArray( config );
//add a listener for termination of the launch
ILaunchManager lmgr = DebugPlugin.getDefault().getLaunchManager();
lmgr.addLaunchListener(new LaunchTerminationWatcher(launch, exePath));
//gmon.out is generated here:
Process process = launcher.execute(exePath, arguments, getEnvironment(config), new Path(workDir.getAbsolutePath()), monitor);
DebugPlugin.newProcess( launch, process, renderProcessLabel( exePath.toOSString() ) );
}
/**
* Check if the pg flag was specified in the configuration. <br>
* If not, prompt user. <br>
* If user wishes to have the option added, add the option and re-build the project. <br>
*/
private boolean preRequisiteCheck() {
/* Tests:
* [x] C Managed Executable
* [x] C++ Managed Executable
*
* [x] C Autotools
* [x] C++ Autotools
*
* 2014.07.09 All Retested after update to shared api.
*/
//Different project types are handled differently. (see this enum for details)
ProjectBuildType projectBuildType = CProjectBuildHelpers.getProjectType(project);
if (projectBuildType == ProjectBuildType.AUTO_TOOLS) {
return preRequisiteCheckAutotools(projectBuildType);
} else if (projectBuildType == ProjectBuildType.MANAGED_MAKEFILE) {
return preRequisiteCheckManagedBuild(projectBuildType);
}
else {
//This is a Makefile project or a project type that we have not checked.
//we don't check flag in this case.
//(there is room for expanding to check other project types thou).
//User will be given a generic prompt in case gmon.out is not produced.
return true;
}
}
private boolean preRequisiteCheckAutotools(ProjectBuildType projectBuildType) {
String optionId = "cflags-gprof"; //$NON-NLS-1$
//See if option was checked.
if (CProjectBuildHelpers.isOptionCheckedInAutotoolsPrefStore(project, optionId)) {
return true;
} else {
return askUserAboutFlag(optionId, projectBuildType);
}
}
private boolean preRequisiteCheckManagedBuild(ProjectBuildType projectBuildType) {
//Distinguish between C & C++.
//Find out what flag should we check for
String optionId = null;
if (CProjectBuildHelpers.isCppType(project)) {
optionId = "gnu.cpp.compiler.option.debugging.gprof"; //$NON-NLS-1$
} else if (CProjectBuildHelpers.isCType(project)) {
optionId = "gnu.c.compiler.option.debugging.gprof"; //$NON-NLS-1$
}
//Check if flag is checked.
if ( CProjectBuildHelpers.isOptionCheckedInCDT(project, optionId) ) {
return true;
} else
return askUserAboutFlag(optionId, projectBuildType);
}
/**
* Tell the user that the required PG flag is not set in the configuration.
* Offer to have it automatically checked.
*/
private boolean askUserAboutFlag(final String optionID, final ProjectBuildType projectBuildType) {
//Construct Message box that will prompt the user.
//Content of message will vary depending on project type
String title = GprofLaunchMessages.GprofMissingFlag_Title;
String msg = GprofLaunchMessages.GprofMissingFlag_Body_shared;
if (projectBuildType == ProjectBuildType.AUTO_TOOLS) {
msg += GprofLaunchMessages.GprofMissingFlag_Body_Autotools;
} else if (projectBuildType == ProjectBuildType.MANAGED_MAKEFILE) {
msg += GprofLaunchMessages.GprofMissingFlag_Body_Managed;
}
msg += GprofLaunchMessages.GprofMissingFlag_BodyPost_autoAddFlagQuestion;
//Open Dialogue.
boolean okPressed = MessageDialogSyncedRunnable.openQuestionSyncedRunnable(title, msg);
if (okPressed) {
enablePgOption(optionID, projectBuildType);
CProjectBuildHelpers.rebuildProject(project);
return true;
} else
return false;
}
private void enablePgOption(String optionId, ProjectBuildType projectBuildType) {
if (projectBuildType == ProjectBuildType.MANAGED_MAKEFILE){
CProjectBuildHelpers.setOptionInCDT(project, optionId, true);
}
else if (projectBuildType == ProjectBuildType.AUTO_TOOLS) {
CProjectBuildHelpers.setOptionInAutotools(project, optionId, "true"); //$NON-NLS-1$
}
}
/*
* Post-Termination.
*/
//A class used to listen for the termination of the current launch, and
//run some functions when it is finished.
class LaunchTerminationWatcher implements ILaunchesListener2 {
private ILaunch launch;
private IPath exePath;
class LaunchTerminationWatcherRunnable implements Runnable {
private String exePath;
private String gmonPath;
public LaunchTerminationWatcherRunnable(String exePath, String gmonPath) {
this.exePath = exePath;
this.gmonPath = gmonPath;
}
@Override
public void run() {
GmonView.displayGprofView(exePath, gmonPath, getProject());
}
}
public LaunchTerminationWatcher(ILaunch il, IPath exePath) {
launch = il;
this.exePath = exePath;
}
/**
* This is ran after the process completes.
*
* <p> It checks if the gmon.out file is available for viewing. <br>
* If it's not there, it prompts the user wit the GprofNoGmonDialog <br>
* Otherwise it opens the gmon viewer. </p>
*/
@Override
public void launchesTerminated(ILaunch[] launches) {
for (ILaunch l : launches) {
/**
* Retrieve the gmon file, and open profiling results.
*/
if (l.equals(launch)) {
//need to run this in the ui thread otherwise get SWT Exceptions
// based on concurrency issues
Display.getDefault().syncExec(() -> {
try {
String s = exePath.toOSString();
RemoteProxyManager rpmgr = RemoteProxyManager.getInstance();
IRemoteFileProxy proxy = rpmgr.getFileProxy(getProject());
// gmon.out file should be in working directory used for the launch.
String gmonExpected = getWorkingDirectory(config).getAbsolutePath() + "/gmon.out"; //$NON-NLS-1$
IFileStore gmonFileStore = proxy.getResource(gmonExpected);
if (!gmonFileStore.fetchInfo().exists()) {
Shell parent1 = PlatformUI.getWorkbench().getDisplay().getActiveShell();
//Missing gmon.out logic:
// This point is reached if pg flags were not set. (e.g in an unmanaged makefile project.)
// or PG flag was set but gmon.out could not be found. (e.g chDir was used by the program
//Prompt user about missing gmon.out
GprofNoGmonDialog noGmonDialog = new GprofNoGmonDialog(project, parent1);
gmonExpected = noGmonDialog.getGmonExpected();
//See if user specified a path to gmon.
if (gmonExpected == null) {
// If user gmon.out selection was bad, we cancle launch.
return;
} else {
// Otherwise we try to retrive the gmon.out file.
gmonFileStore = proxy.getResource(gmonExpected);
}
}
// We found gmon.out. Make sure it was generated after the executable was formed.
IFileStore exe = proxy.getResource(exePath.toString());
if (exe.fetchInfo().getLastModified() > gmonFileStore.fetchInfo().getLastModified()) {
String title = GprofLaunchMessages.GprofGmonStale_msg;
String message = GprofLaunchMessages.GprofGmonStaleExplanation_msg;
Shell parent2 = PlatformUI.getWorkbench().getDisplay().getActiveShell();
MessageDialog.openWarning(parent2, title, message);
}
Display.getDefault().asyncExec(new LaunchTerminationWatcherRunnable(s, gmonExpected));
} catch (NullPointerException e1) {
// Do nothing
} catch (CoreException e2) {
// Do nothing
}
});
}
}
}
@Override
public void launchesAdded(ILaunch[] launches) { /* dont care */}
@Override
public void launchesChanged(ILaunch[] launches) { /* dont care */ }
@Override
public void launchesRemoved(ILaunch[] launches) { /* dont care */ }
}
@Override
protected String getPluginID() {
return GprofLaunch.PLUGIN_ID;
}
/* all these functions exist to be overridden by the test class in order to allow launch testing */
private IProject getProject(){
try{
return CDebugUtils.verifyCProject(config).getProject();
} catch (CoreException e) {
e.printStackTrace();
}
return null;
}
/**
*
* Return the exe path of the binary to be profiled.
* @param config
* @return the exe path of the binary stored in the configuration
* @throws CoreException
* @since 1.1
*/
private static IPath getExePath(ILaunchConfiguration config) throws CoreException{
return CDebugUtils.verifyProgramPath( config );
}
}