| /******************************************************************************* |
| * 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 ); |
| } |
| |
| } |