/******************************************************************************* * Copyright (c) 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 *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.service; import java.util.Map; import java.util.Properties; import org.eclipse.cdt.debug.core.CDebugUtils; import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; import org.eclipse.cdt.dsf.concurrent.DsfExecutor; import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; 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.datamodel.DMContexts; import org.eclipse.cdt.dsf.datamodel.IDMContext; import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext; import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; import org.eclipse.cdt.dsf.gdb.launching.LaunchMessages; import org.eclipse.cdt.dsf.gdb.service.IGDBTraceControl.ITraceTargetDMContext; import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl; import org.eclipse.cdt.dsf.mi.service.IMICommandControl; import org.eclipse.cdt.dsf.mi.service.IMIContainerDMContext; import org.eclipse.cdt.dsf.mi.service.MIBreakpointsManager; import org.eclipse.cdt.dsf.mi.service.MIProcesses; 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.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.IStatusHandler; /** * This sequence is used to start debugging a new process. * * @since 4.0 */ public class DebugNewProcessSequence extends ReflectionSequence { private final static String INVALID = "invalid"; //$NON-NLS-1$ private IGDBControl fCommandControl; private CommandFactory fCommandFactory; private IGDBBackend fBackend; private IGDBProcesses fProcService; private DsfServicesTracker fTracker; private IDMContext fContext; private String fBinaryName; private Map<String, Object> fAttributes; private IMIContainerDMContext fContainerCtx; // Store the dataRM so that we can fill it with the container context that we will be creating private DataRequestMonitor<IDMContext> fDataRequestMonitor; protected IMIContainerDMContext getContainerContext() { return fContainerCtx; } protected void setContainerContext(IMIContainerDMContext ctx) { fContainerCtx = ctx; } public DebugNewProcessSequence(DsfExecutor executor, boolean isInitial, IDMContext dmc, String file, Map<String, Object> attributes, DataRequestMonitor<IDMContext> rm) { super(executor, rm); fContext = dmc; fBinaryName = file; fAttributes = attributes; fDataRequestMonitor = rm; } @Override protected String[] getExecutionOrder(String group) { if (GROUP_TOP_LEVEL.equals(group)) { return new String[] { "stepInitializeBaseSequence", //$NON-NLS-1$ "stepSetEnvironmentVariables", //$NON-NLS-1$ "stepSetExecutable", //$NON-NLS-1$ "stepSetArguments", //$NON-NLS-1$ // For remote non-attach only "stepRemoteConnection", //$NON-NLS-1$ // For post-mortem launch only "stepSpecifyCoreFile", //$NON-NLS-1$ "stepStartTrackingBreakpoints", //$NON-NLS-1$ "stepStartExecution", //$NON-NLS-1$ "stepCleanupBaseSequence", //$NON-NLS-1$ }; } return null; } /** * Initialize the members of the DebugNewProcessSequence class. * This step is mandatory for the rest of the sequence to complete. */ @Execute public void stepInitializeBaseSequence(RequestMonitor rm) { fTracker = new DsfServicesTracker(GdbPlugin.getBundleContext(), fContext.getSessionId()); fBackend = fTracker.getService(IGDBBackend.class); fCommandControl = fTracker.getService(IGDBControl.class); fCommandFactory = fTracker.getService(IMICommandControl.class).getCommandFactory(); fProcService = fTracker.getService(IGDBProcesses.class); if (fBackend == null || fCommandControl == null || fCommandFactory == null || fProcService == null) { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Cannot obtain service", null)); //$NON-NLS-1$ rm.done(); return; } // When we are starting to debug a new process, the container is the default process used by GDB. // We don't have a pid yet, so we can simply create the container with the UNIQUE_GROUP_ID setContainerContext(fProcService.createContainerContextFromGroupId(fCommandControl.getContext(), MIProcesses.UNIQUE_GROUP_ID)); rm.done(); } /** * Rollback method for {@link #stepInitializeBaseSequence()} * @since 4.0 */ @RollBack("stepInitializeBaseSequence") public void rollBackInitializeBaseSequence(RequestMonitor rm) { if (fTracker != null) fTracker.dispose(); fTracker = null; rm.done(); } /** * Specify environment variables if needed */ @Execute public void stepSetEnvironmentVariables(RequestMonitor rm) { boolean clear = false; Properties properties = new Properties(); try { // here we need to pass the proper container context clear = fBackend.getClearEnvironment(); properties = fBackend.getEnvironmentVariables(); } catch (CoreException e) { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "Cannot get environment information", e)); //$NON-NLS-1$ rm.done(); return; } if (clear == true || properties.size() > 0) { // here we need to pass the proper container context fCommandControl.setEnvironment(properties, clear, rm); } else { rm.done(); } } /** * Specify the executable file to be debugged and read the symbol table. */ @Execute public void stepSetExecutable(RequestMonitor rm) { boolean noFileCommand = CDebugUtils.getAttribute( fAttributes, IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_USE_SOLIB_SYMBOLS_FOR_APP, IGDBLaunchConfigurationConstants.DEBUGGER_USE_SOLIB_SYMBOLS_FOR_APP_DEFAULT); if (!noFileCommand && fBinaryName != null && fBinaryName.length() > 0) { fCommandControl.queueCommand( fCommandFactory.createMIFileExecAndSymbols(getContainerContext(), fBinaryName), new DataRequestMonitor<MIInfo>(ImmediateExecutor.getInstance(), rm)); } else { rm.done(); } } /** * Specify the arguments to the program that will be run. */ @Execute public void stepSetArguments(RequestMonitor rm) { try { String args = fBackend.getProgramArguments(); if (args != null) { String[] argArray = args.replaceAll("\n", " ").split(" "); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ fCommandControl.queueCommand( fCommandFactory.createMIGDBSetArgs(getContainerContext(), argArray), new DataRequestMonitor<MIInfo>(ImmediateExecutor.getInstance(), rm)); } else { rm.done(); } } catch (CoreException e) { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "Cannot get inferior arguments", e)); //$NON-NLS-1$ rm.done(); } } /** * If we are dealing with a remote debugging session, connect to the target. * @since 4.0 */ @Execute public void stepRemoteConnection(RequestMonitor rm) { // If we are dealing with a non-attach remote session, it is now time to connect // to the remote side. Note that this is the 'target remote' case // and not the 'target extended-remote' case (remote attach session) // This step is actually global for GDB. However, we have to do it after // we have specified the executable, so we have to do it here. // It is safe to do it here because a 'target remote' does not support // multi-process so this step will not be executed more than once. if (fBackend.getSessionType() == SessionType.REMOTE && !fBackend.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, false), new DataRequestMonitor<MIInfo>(ImmediateExecutor.getInstance(), rm)); } else { String serialDevice = CDebugUtils.getAttribute( fAttributes, IGDBLaunchConfigurationConstants.ATTR_DEV, INVALID); fCommandControl.queueCommand( fCommandFactory.createMITargetSelect(fCommandControl.getContext(), serialDevice, false), new DataRequestMonitor<MIInfo>(ImmediateExecutor.getInstance(), rm)); } } else { rm.done(); } } /** @since 4.0 */ protected static class PromptForCoreJob extends Job { protected DataRequestMonitor<String> fRequestMonitor; public PromptForCoreJob(String name, DataRequestMonitor<String> rm) { super(name); fRequestMonitor = rm; } @Override protected IStatus run(IProgressMonitor monitor) { final IStatus promptStatus = new Status(IStatus.INFO, "org.eclipse.debug.ui", 200/*STATUS_HANDLER_PROMPT*/, "", null); //$NON-NLS-1$//$NON-NLS-2$ final IStatus filePrompt = new Status(IStatus.INFO, "org.eclipse.cdt.dsf.gdb.ui", 1001, "", null); //$NON-NLS-1$//$NON-NLS-2$ // consult a status handler final IStatusHandler prompter = DebugPlugin.getDefault().getStatusHandler(promptStatus); final Status NO_CORE_STATUS = new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, LaunchMessages.getString("LocalCDILaunchDelegate.6"), //$NON-NLS-1$ null); if (prompter == null) { fRequestMonitor.setStatus(NO_CORE_STATUS); fRequestMonitor.done(); return Status.OK_STATUS; } try { Object result = prompter.handleStatus(filePrompt, null); if (result == null) { fRequestMonitor.cancel(); } else if (result instanceof String) { fRequestMonitor.setData((String)result); } else { fRequestMonitor.setStatus(NO_CORE_STATUS); } } catch (CoreException e) { fRequestMonitor.setStatus(NO_CORE_STATUS); } fRequestMonitor.done(); return Status.OK_STATUS; } }; /** * If we are dealing with a postmortem session, connect to the core/trace file. * @since 4.0 */ @Execute public void stepSpecifyCoreFile(final RequestMonitor rm) { // If we are dealing with a postmortem session, it is now time to connect // to the core/trace file. We have to do this step after // we have specified the executable, so we have to do it here. // It is safe to do it here because a postmortem session does not support // multi-process so this step will not be executed more than once. // Bug 338730 if (fBackend.getSessionType() == SessionType.CORE) { String coreFile = CDebugUtils.getAttribute( fAttributes, ICDTLaunchConfigurationConstants.ATTR_COREFILE_PATH, ""); //$NON-NLS-1$ final String coreType = CDebugUtils.getAttribute( fAttributes, IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_POST_MORTEM_TYPE, IGDBLaunchConfigurationConstants.DEBUGGER_POST_MORTEM_TYPE_DEFAULT); if (coreFile.length() == 0) { new PromptForCoreJob( "Prompt for post mortem file", //$NON-NLS-1$ new DataRequestMonitor<String>(getExecutor(), rm) { @Override protected void handleCancel() { rm.cancel(); rm.done(); } @Override protected void handleSuccess() { String newCoreFile = getData(); if (newCoreFile == null || newCoreFile.length()== 0) { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot get post mortem file path", null)); //$NON-NLS-1$ rm.done(); } else { if (coreType.equals(IGDBLaunchConfigurationConstants.DEBUGGER_POST_MORTEM_CORE_FILE)) { fCommandControl.queueCommand( fCommandFactory.createMITargetSelectCore(fCommandControl.getContext(), newCoreFile), new DataRequestMonitor<MIInfo>(getExecutor(), rm)); } else if (coreType.equals(IGDBLaunchConfigurationConstants.DEBUGGER_POST_MORTEM_TRACE_FILE)) { IGDBTraceControl traceControl = fTracker.getService(IGDBTraceControl.class); if (traceControl != null) { ITraceTargetDMContext targetDmc = DMContexts.getAncestorOfType(fCommandControl.getContext(), ITraceTargetDMContext.class); traceControl.loadTraceData(targetDmc, newCoreFile, rm); } else { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Tracing not supported", null)); rm.done(); } } else { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Invalid post-mortem type", null)); rm.done(); } } } }).schedule(); } else { if (coreType.equals(IGDBLaunchConfigurationConstants.DEBUGGER_POST_MORTEM_CORE_FILE)) { fCommandControl.queueCommand( fCommandFactory.createMITargetSelectCore(fCommandControl.getContext(), coreFile), new DataRequestMonitor<MIInfo>(getExecutor(), rm)); } else if (coreType.equals(IGDBLaunchConfigurationConstants.DEBUGGER_POST_MORTEM_TRACE_FILE)) { IGDBTraceControl traceControl = fTracker.getService(IGDBTraceControl.class); if (traceControl != null) { ITraceTargetDMContext targetDmc = DMContexts.getAncestorOfType(fCommandControl.getContext(), ITraceTargetDMContext.class); traceControl.loadTraceData(targetDmc, coreFile, rm); } else { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Tracing not supported", null)); rm.done(); } } else { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Invalid post-mortem type", null)); rm.done(); } } } else { rm.done(); } } /** * Start tracking the breakpoints. Note that for remote debugging * we should first connect to the target. */ @Execute public void stepStartTrackingBreakpoints(RequestMonitor rm) { if (fBackend.getSessionType() != SessionType.CORE) { MIBreakpointsManager bpmService = fTracker.getService(MIBreakpointsManager.class); IBreakpointsTargetDMContext bpTargetDmc = DMContexts.getAncestorOfType(getContainerContext(), IBreakpointsTargetDMContext.class); bpmService.startTrackingBreakpoints(bpTargetDmc, rm); } else { rm.done(); } } /** * Start executing the program. */ @Execute public void stepStartExecution(final RequestMonitor rm) { if (fBackend.getSessionType() != SessionType.CORE) { // Overwrite the program name to use the binary name that was specified. // This is important for multi-process // Bug 342351 fAttributes.put(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, fBinaryName); fProcService.start(getContainerContext(), fAttributes, new DataRequestMonitor<IContainerDMContext>(ImmediateExecutor.getInstance(), rm) { @Override protected void handleSuccess() { assert getData() instanceof IMIContainerDMContext; // Set the container that we created setContainerContext(DMContexts.getAncestorOfType(getData(), IMIContainerDMContext.class)); fDataRequestMonitor.setData(getContainerContext()); // Don't call fDataRequestMonitor.done(), the sequence will // automatically do that when it completes; rm.done(); } }); } else { fDataRequestMonitor.setData(getContainerContext()); rm.done(); } } /** * Cleanup now that the sequence has been run. * @since 4.0 */ @Execute public void stepCleanupBaseSequence(RequestMonitor rm) { fTracker.dispose(); fTracker = null; rm.done(); } }