/******************************************************************************* * Copyright (c) 2008, 2011 Ericsson 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: * Ericsson - initial API and implementation * Nokia - create and use backend service. * IBM Corporation * Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) * Sergey Prigogin (Google) *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.launching; import java.util.List; import java.util.Map; import org.eclipse.cdt.debug.core.CDebugUtils; import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; import org.eclipse.cdt.debug.internal.core.sourcelookup.CSourceLookupDirector; import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor; import org.eclipse.cdt.dsf.concurrent.ReflectionSequence; import org.eclipse.cdt.dsf.concurrent.RequestMonitor; import org.eclipse.cdt.dsf.concurrent.RequestMonitorWithProgress; import org.eclipse.cdt.dsf.datamodel.DataModelInitializedEvent; import org.eclipse.cdt.dsf.datamodel.IDMContext; import org.eclipse.cdt.dsf.debug.service.ISourceLookup.ISourceLookupDMContext; import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; import org.eclipse.cdt.dsf.gdb.IGdbDebugPreferenceConstants; import org.eclipse.cdt.dsf.gdb.actions.IConnect; import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; import org.eclipse.cdt.dsf.gdb.service.IGDBBackend; import org.eclipse.cdt.dsf.gdb.service.SessionType; import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl; import org.eclipse.cdt.dsf.mi.service.CSourceLookup; import org.eclipse.cdt.dsf.mi.service.IMIProcesses; import org.eclipse.cdt.dsf.mi.service.command.CommandFactory; import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; import org.eclipse.cdt.dsf.service.DsfServicesTracker; import org.eclipse.cdt.dsf.service.DsfSession; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.debug.core.ILaunch; public class FinalLaunchSequence extends ReflectionSequence { // The launchConfiguration attributes private Map<String, Object> fAttributes; private IGDBControl fCommandControl; private IGDBBackend fGDBBackend; private IMIProcesses fProcService; private CommandFactory fCommandFactory; private DsfServicesTracker fTracker; private DsfSession fSession; /** * @since 4.0 */ public FinalLaunchSequence(DsfSession session, Map<String, Object> attributes, RequestMonitorWithProgress rm) { super(session.getExecutor(), rm, LaunchMessages.getString("FinalLaunchSequence.0"), LaunchMessages.getString("FinalLaunchSequence.1")); //$NON-NLS-1$ //$NON-NLS-2$ fSession = session; fAttributes = attributes; } @Override protected String[] getExecutionOrder(String group) { if (GROUP_TOP_LEVEL.equals(group)) { return new String[] { "stepInitializeFinalLaunchSequence", //$NON-NLS-1$ // Global GDB settings "stepSetEnvironmentDirectory", //$NON-NLS-1$ "stepSetBreakpointPending", //$NON-NLS-1$ "stepEnablePrettyPrinting", //$NON-NLS-1$ "stepSourceGDBInitFile", //$NON-NLS-1$ "stepSetNonStop", //$NON-NLS-1$ "stepSetAutoLoadSharedLibrarySymbols", //$NON-NLS-1$ "stepSetSharedLibraryPaths", //$NON-NLS-1$ // -environment-directory with a lot of paths could // make setting breakpoint incredibly slow, which makes // the debug session un-workable. We simply stop // using it because it's usefulness is unclear. // Bug 225805 // // "stepSetSourceLookupPath", //$NON-NLS-1$ // For remote-attach launch only "stepRemoteConnection", //$NON-NLS-1$ // For all launches except attach ones "stepNewProcess", //$NON-NLS-1$ // For local attach launch only "stepAttachToProcess", //$NON-NLS-1$ // Global "stepDataModelInitializationComplete", //$NON-NLS-1$ "stepCleanup", //$NON-NLS-1$ }; } return null; } /** * Initialize the members of the FinalLaunchSequence class. * This step is mandatory for the rest of the sequence to complete. * @since 4.0 */ @Execute public void stepInitializeFinalLaunchSequence(RequestMonitor requestMonitor) { fTracker = new DsfServicesTracker(GdbPlugin.getBundleContext(), fSession.getId()); fGDBBackend = fTracker.getService(IGDBBackend.class); if (fGDBBackend == null) { requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot obtain GDBBackend service", null)); //$NON-NLS-1$ requestMonitor.done(); return; } fCommandControl = fTracker.getService(IGDBControl.class); if (fCommandControl == null) { requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot obtain control service", null)); //$NON-NLS-1$ requestMonitor.done(); return; } fCommandFactory = fCommandControl.getCommandFactory(); fProcService = fTracker.getService(IMIProcesses.class); if (fProcService == null) { requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot obtain process service", null)); //$NON-NLS-1$ requestMonitor.done(); return; } requestMonitor.done(); } /** * Rollback method for {@link #stepInitializeFinalLaunchSequence()} * @since 4.0 */ @RollBack("stepInitializeFinalLaunchSequence") public void rollBackInitializeFinalLaunchSequence(RequestMonitor requestMonitor) { if (fTracker != null) fTracker.dispose(); fTracker = null; requestMonitor.done(); } /** * Specify GDB's working directory. * @since 4.0 */ @Execute public void stepSetEnvironmentDirectory(final RequestMonitor requestMonitor) { IPath dir = null; try { dir = fGDBBackend.getGDBWorkingDirectory(); } catch (CoreException e) { requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot get working directory", e)); //$NON-NLS-1$ requestMonitor.done(); return; } if (dir != null) { fCommandControl.queueCommand( fCommandFactory.createMIEnvironmentCD(fCommandControl.getContext(), dir.toPortableString()), new DataRequestMonitor<MIInfo>(getExecutor(), requestMonitor)); } else { requestMonitor.done(); } } /** * Allow breakpoints/tracepoints to be set as pending when using the gdb console * or a CLI command to create them. * @since 4.0 */ @Execute public void stepSetBreakpointPending(final RequestMonitor requestMonitor) { if (fGDBBackend.getSessionType() != SessionType.CORE) { fCommandControl.queueCommand( fCommandFactory.createMIGDBSetBreakpointPending(fCommandControl.getContext(), true), new DataRequestMonitor<MIInfo>(getExecutor(), requestMonitor)); } else { requestMonitor.done(); } } /** * Turn on pretty printers for MI variable objects, if enabled in preferences. * Also, turn off error messages from python, all the time. * @since 4.0 */ @Execute public void stepEnablePrettyPrinting(final RequestMonitor requestMonitor) { if (Platform.getPreferencesService().getBoolean(GdbPlugin.PLUGIN_ID, IGdbDebugPreferenceConstants.PREF_ENABLE_PRETTY_PRINTING, false, null)) { fCommandControl.enablePrettyPrintingForMIVariableObjects( new RequestMonitor(getExecutor(), requestMonitor) { @Override protected void handleCompleted() { fCommandControl.setPrintPythonErrors(false, requestMonitor); } }); } else { fCommandControl.setPrintPythonErrors(false, requestMonitor); } } /** * Source the gdbinit file specified in the launch. * @since 4.0 */ @Execute public void stepSourceGDBInitFile(final RequestMonitor requestMonitor) { try { final String gdbinitFile = fGDBBackend.getGDBInitFile(); if (gdbinitFile != null && gdbinitFile.length() > 0) { fCommandControl.queueCommand( fCommandFactory.createCLISource(fCommandControl.getContext(), gdbinitFile), new DataRequestMonitor<MIInfo>(getExecutor(), requestMonitor) { @Override protected void handleCompleted() { // If the gdbinitFile is the default, then it may not exist and we // should not consider this an error. // If it is not the default, then the user must have specified it and // we want to warn the user if we can't find it. if (!gdbinitFile.equals(IGDBLaunchConfigurationConstants.DEBUGGER_GDB_INIT_DEFAULT )) { requestMonitor.setStatus(getStatus()); } requestMonitor.done(); } }); } else { requestMonitor.done(); } } catch (CoreException e) { requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot get gdbinit option", e)); //$NON-NLS-1$ requestMonitor.done(); } } /** * Enable non-stop mode if requested. * @since 4.0 */ @Execute public void stepSetNonStop(final RequestMonitor requestMonitor) { boolean isNonStop = CDebugUtils.getAttribute( fAttributes, IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_NON_STOP, LaunchUtils.getIsNonStopModeDefault()); // GDBs that don't support non-stop don't allow you to set it to false. // We really should set it to false when GDB supports it though. // Something to fix later. if (isNonStop) { fCommandControl.queueCommand( fCommandFactory.createMIGDBSetTargetAsync(fCommandControl.getContext(), true), new DataRequestMonitor<MIInfo>(getExecutor(), requestMonitor) { @Override protected void handleSuccess() { fCommandControl.queueCommand( fCommandFactory.createMIGDBSetPagination(fCommandControl.getContext(), false), new DataRequestMonitor<MIInfo>(getExecutor(), requestMonitor) { @Override protected void handleSuccess() { fCommandControl.queueCommand( fCommandFactory.createMIGDBSetNonStop(fCommandControl.getContext(), true), new DataRequestMonitor<MIInfo>(getExecutor(), requestMonitor)); } }); } }); } else { requestMonitor.done(); } } /** * Tell GDB to automatically load or not the shared library symbols * @since 4.0 */ @Execute public void stepSetAutoLoadSharedLibrarySymbols(RequestMonitor requestMonitor) { boolean autolib = CDebugUtils.getAttribute( fAttributes, IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_AUTO_SOLIB, IGDBLaunchConfigurationConstants.DEBUGGER_AUTO_SOLIB_DEFAULT); fCommandControl.queueCommand( fCommandFactory.createMIGDBSetAutoSolib(fCommandControl.getContext(), autolib), new DataRequestMonitor<MIInfo>(getExecutor(), requestMonitor)); } /** * Set the shared library paths. * @since 4.0 */ @Execute public void stepSetSharedLibraryPaths(final RequestMonitor requestMonitor) { try { List<String> p = fGDBBackend.getSharedLibraryPaths(); if (p.size() > 0) { String[] paths = p.toArray(new String[p.size()]); fCommandControl.queueCommand( fCommandFactory.createMIGDBSetSolibSearchPath(fCommandControl.getContext(), paths), new DataRequestMonitor<MIInfo>(getExecutor(), requestMonitor) { @Override protected void handleSuccess() { // Sysroot is not available in GDB6.6 and will make the launch fail in that case. // Let's remove it for now requestMonitor.done(); // // If we are able to set the solib-search-path, // // we should disable the sysroot variable, as indicated // // in the GDB documentation. This is to avoid the sysroot // // variable finding libraries that were not meant to be found. // fCommandControl.queueCommand( // new MIGDBSetSysroot(fCommandControl.getContext()), // new DataRequestMonitor<MIInfo>(getExecutor(), requestMonitor)); }; }); } else { requestMonitor.done(); } } catch (CoreException e) { requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot set share library paths", e)); //$NON-NLS-1$ requestMonitor.done(); } } /** * Setup the source paths. * @since 4.0 */ @Execute public void stepSetSourceLookupPath(RequestMonitor requestMonitor) { CSourceLookup sourceLookup = fTracker.getService(CSourceLookup.class); ILaunch launch = (ILaunch)fSession.getModelAdapter(ILaunch.class); CSourceLookupDirector locator = (CSourceLookupDirector)launch.getSourceLocator(); ISourceLookupDMContext sourceLookupDmc = (ISourceLookupDMContext)fCommandControl.getContext(); sourceLookup.setSourceLookupPath(sourceLookupDmc, locator.getSourceContainers(), requestMonitor); } private final static String INVALID = "invalid"; //$NON-NLS-1$ /** * If we are dealing with a remote-attach debugging session, connect to the target. * @since 4.0 */ @Execute public void stepRemoteConnection(final RequestMonitor rm) { if (fGDBBackend.getSessionType() == SessionType.REMOTE && fGDBBackend.getIsAttachSession()) { boolean isTcpConnection = CDebugUtils.getAttribute( fAttributes, IGDBLaunchConfigurationConstants.ATTR_REMOTE_TCP, false); if (isTcpConnection) { String remoteTcpHost = CDebugUtils.getAttribute( fAttributes, IGDBLaunchConfigurationConstants.ATTR_HOST, INVALID); String remoteTcpPort = CDebugUtils.getAttribute( fAttributes, IGDBLaunchConfigurationConstants.ATTR_PORT, INVALID); fCommandControl.queueCommand( fCommandFactory.createMITargetSelect(fCommandControl.getContext(), remoteTcpHost, remoteTcpPort, true), new DataRequestMonitor<MIInfo>(ImmediateExecutor.getInstance(), rm)); } else { String serialDevice = CDebugUtils.getAttribute( fAttributes, IGDBLaunchConfigurationConstants.ATTR_DEV, INVALID); fCommandControl.queueCommand( fCommandFactory.createMITargetSelect(fCommandControl.getContext(), serialDevice, true), new DataRequestMonitor<MIInfo>(ImmediateExecutor.getInstance(), rm)); } } else { rm.done(); } } /** * Start a new process if we are not dealing with an attach session * i.e., a local session, a remote session or a post-mortem (core) session. * @since 4.0 */ @Execute public void stepNewProcess(final RequestMonitor rm) { if (!fGDBBackend.getIsAttachSession()) { boolean noBinarySpecified = CDebugUtils.getAttribute( fAttributes, IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_USE_SOLIB_SYMBOLS_FOR_APP, IGDBLaunchConfigurationConstants.DEBUGGER_USE_SOLIB_SYMBOLS_FOR_APP_DEFAULT); String binary = null; final IPath execPath = fGDBBackend.getProgramPath(); if (!noBinarySpecified && execPath != null && !execPath.isEmpty()) { binary = execPath.toPortableString(); } // Even if binary is null, we must call this to do all the other steps // necessary to create a process. It is possible that the binary is not needed fProcService.debugNewProcess(fCommandControl.getContext(), binary, fAttributes, new DataRequestMonitor<IDMContext>(getExecutor(), rm) { @Override protected void handleCancel() { // If this step is cancelled, cancel the current sequence. // This is to allow the user to press the cancel button // when prompted for a post-mortem file. // Bug 362105 rm.cancel(); rm.done(); } }); } else { rm.done(); } } /** * If we are dealing with an local attach session, perform the attach. * For a remote attach session, we don't attach during the launch; instead * we wait for the user to manually do the attach. * @since 4.0 */ @Execute public void stepAttachToProcess(final RequestMonitor requestMonitor) { if (fGDBBackend.getIsAttachSession() && fGDBBackend.getSessionType() != SessionType.REMOTE) { // Is the process id already stored in the launch? int pid = CDebugUtils.getAttribute( fAttributes, ICDTLaunchConfigurationConstants.ATTR_ATTACH_PROCESS_ID, -1); if (pid != -1) { fProcService.attachDebuggerToProcess( fProcService.createProcessContext(fCommandControl.getContext(), Integer.toString(pid)), new DataRequestMonitor<IDMContext>(getExecutor(), requestMonitor)); } else { IConnect connectCommand = (IConnect)fSession.getModelAdapter(IConnect.class); if (connectCommand != null) { connectCommand.connect(requestMonitor); } else { requestMonitor.done(); } } } else { requestMonitor.done(); } } /** * Indicate that the Data Model has been filled. This will trigger the Debug view to expand. * @since 4.0 */ @Execute public void stepDataModelInitializationComplete(final RequestMonitor requestMonitor) { fSession.dispatchEvent(new DataModelInitializedEvent(fCommandControl.getContext()), fCommandControl.getProperties()); requestMonitor.done(); } /** * Cleanup now that the sequence has been run. * @since 4.0 */ @Execute public void stepCleanup(final RequestMonitor requestMonitor) { fTracker.dispose(); fTracker = null; requestMonitor.done(); } }