/******************************************************************************* * Copyright (c) 2006, 2011 Wind River Systems 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: * Wind River Systems - initial API and implementation *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.launching; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.RejectedExecutionException; import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; import org.eclipse.cdt.dsf.concurrent.DefaultDsfExecutor; import org.eclipse.cdt.dsf.concurrent.DsfExecutor; import org.eclipse.cdt.dsf.concurrent.DsfRunnable; import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor; import org.eclipse.cdt.dsf.concurrent.RequestMonitor; import org.eclipse.cdt.dsf.concurrent.Sequence; import org.eclipse.cdt.dsf.concurrent.ThreadSafe; import org.eclipse.cdt.dsf.concurrent.ThreadSafeAndProhibitedFromDsfExecutor; import org.eclipse.cdt.dsf.debug.model.DsfLaunch; import org.eclipse.cdt.dsf.debug.model.DsfMemoryBlockRetrieval; import org.eclipse.cdt.dsf.debug.service.IDsfDebugServicesFactory; import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryDMContext; import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext; import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlShutdownDMEvent; import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; import org.eclipse.cdt.dsf.gdb.internal.memory.GdbMemoryBlockRetrieval; import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl; import org.eclipse.cdt.dsf.mi.service.IMIProcesses; import org.eclipse.cdt.dsf.mi.service.MIProcesses; import org.eclipse.cdt.dsf.mi.service.command.AbstractCLIProcess; import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; 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.IStatus; import org.eclipse.core.runtime.MultiStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.debug.core.DebugEvent; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.model.IDisconnect; import org.eclipse.debug.core.model.IMemoryBlockRetrieval; import org.eclipse.debug.core.model.ISourceLocator; import org.eclipse.debug.core.model.ITerminate; /** * The only object in the model that implements the traditional interfaces. */ @ThreadSafe public class GdbLaunch extends DsfLaunch implements ITerminate, IDisconnect, ITracedLaunch { private DefaultDsfExecutor fExecutor; private DsfSession fSession; private DsfServicesTracker fTracker; private boolean fInitialized = false; private boolean fShutDown = false; private DsfMemoryBlockRetrieval fMemRetrieval; private IDsfDebugServicesFactory fServiceFactory; public GdbLaunch(ILaunchConfiguration launchConfiguration, String mode, ISourceLocator locator) { super(launchConfiguration, mode, locator); // Create the dispatch queue to be used by debugger control and services // that belong to this launch final DefaultDsfExecutor dsfExecutor = new DefaultDsfExecutor(GdbLaunchDelegate.GDB_DEBUG_MODEL_ID); // Bug 293109 comment 14 // We can use delayed task on the executor, but once it is shutdown, we don't want to execute them. // For instance, in GDBBackend, we start a 30-second delayed task to check that GDB properly started. dsfExecutor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false); dsfExecutor.prestartCoreThread(); fExecutor = dsfExecutor; fSession = DsfSession.startSession(fExecutor, GdbLaunchDelegate.GDB_DEBUG_MODEL_ID); } public DsfExecutor getDsfExecutor() { return fExecutor; } public IDsfDebugServicesFactory getServiceFactory() { return fServiceFactory; } public void initialize() { /* * Registering the launch as an adapter. This ensures that this launch * will be associated with all DMContexts from this session. * We do this here because we want to have access to the launch even * if we run headless, but when we run headless, GdbAdapterFactory is * not initialized. */ fSession.registerModelAdapter(ILaunch.class, this); Runnable initRunnable = new DsfRunnable() { public void run() { fTracker = new DsfServicesTracker(GdbPlugin.getBundleContext(), fSession.getId()); fSession.addServiceEventListener(GdbLaunch.this, null); fInitialized = true; fireChanged(); } }; // Invoke the execution code and block waiting for the result. try { fExecutor.submit(initRunnable).get(); } catch (InterruptedException e) { new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Error initializing launch", e); //$NON-NLS-1$ } catch (ExecutionException e) { new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Error initializing launch", e); //$NON-NLS-1$ } } public void initializeControl() throws CoreException { // Create a memory retrieval and register it with the session try { fExecutor.submit( new Callable<Object>() { public Object call() throws CoreException { ICommandControlService commandControl = fTracker.getService(ICommandControlService.class); IMIProcesses procService = fTracker.getService(IMIProcesses.class); if (commandControl != null && procService != null) { fMemRetrieval = new GdbMemoryBlockRetrieval( GdbLaunchDelegate.GDB_DEBUG_MODEL_ID, getLaunchConfiguration(), fSession); fSession.registerModelAdapter(IMemoryBlockRetrieval.class, fMemRetrieval); IProcessDMContext procDmc = procService.createProcessContext(commandControl.getContext(), MIProcesses.UNKNOWN_PROCESS_ID); IMemoryDMContext memoryDmc = (IMemoryDMContext)procService.createContainerContext(procDmc, MIProcesses.UNIQUE_GROUP_ID); fMemRetrieval.initialize(memoryDmc); } return null; } }).get(); } catch (InterruptedException e) { throw new CoreException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, 0, "Interrupted while waiting for get process callable.", e)); //$NON-NLS-1$ } catch (ExecutionException e) { throw (CoreException)e.getCause(); } catch (RejectedExecutionException e) { throw new CoreException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, 0, "Debugger shut down before launch was completed.", e)); //$NON-NLS-1$ } } public DsfSession getSession() { return fSession; } @ThreadSafeAndProhibitedFromDsfExecutor("getDsfExecutor()") public void addCLIProcess(String label) throws CoreException { try { // Add the CLI process object to the launch. AbstractCLIProcess cliProc = getDsfExecutor().submit( new Callable<AbstractCLIProcess>() { public AbstractCLIProcess call() throws CoreException { IGDBControl gdb = fTracker.getService(IGDBControl.class); if (gdb != null) { return gdb.getCLIProcess(); } return null; } }).get(); GDBProcess gdbProcess = new GDBProcess(this, cliProc, label, null); addProcess(gdbProcess); } catch (InterruptedException e) { throw new CoreException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, 0, "Interrupted while waiting for get process callable.", e)); //$NON-NLS-1$ } catch (ExecutionException e) { throw (CoreException)e.getCause(); } catch (RejectedExecutionException e) { throw new CoreException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, 0, "Debugger shut down before launch was completed.", e)); //$NON-NLS-1$ } } public void setServiceFactory(IDsfDebugServicesFactory factory) { fServiceFactory = factory; } /////////////////////////////////////////////////////////////////////////// // IServiceEventListener @DsfServiceEventHandler public void eventDispatched(ICommandControlShutdownDMEvent event) { shutdownSession(new RequestMonitor(ImmediateExecutor.getInstance(), null)); } /////////////////////////////////////////////////////////////////////////// // ITerminate @Override public boolean canTerminate() { return fInitialized && super.canTerminate(); } // ITerminate /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// // IDisconnect @Override public boolean canDisconnect() { return canTerminate(); } @Override public boolean isDisconnected() { return isTerminated(); } @Override public void disconnect() throws DebugException { terminate(); } // IDisconnect /////////////////////////////////////////////////////////////////////////// /** * Shuts down the services, the session and the executor associated with * this launch. * <p> * Note: The argument request monitor to this method should NOT use the * executor that belongs to this launch. By the time the shutdown is * complete, this executor will not be dispatching anymore and the * request monitor will never be invoked. Instead callers should use * the {@link ImmediateExecutor}. * </p> * @param rm The request monitor invoked when the shutdown is complete. */ @ConfinedToDsfExecutor("getSession().getExecutor()") public void shutdownSession(final RequestMonitor rm) { if (fShutDown) { rm.done(); return; } fShutDown = true; Sequence shutdownSeq = new ShutdownSequence( getDsfExecutor(), fSession.getId(), new RequestMonitor(fSession.getExecutor(), rm) { @Override public void handleCompleted() { fSession.removeServiceEventListener(GdbLaunch.this); if (!isSuccess()) { GdbPlugin.getDefault().getLog().log(new MultiStatus( GdbPlugin.PLUGIN_ID, -1, new IStatus[]{getStatus()}, "Session shutdown failed", null)); //$NON-NLS-1$ } // Last order of business, shutdown the dispatch queue. fTracker.dispose(); fTracker = null; DsfSession.endSession(fSession); // DsfMemoryBlockRetrieval.saveMemoryBlocks(); fMemRetrieval.saveMemoryBlocks(); // Fire a terminate event for the memory retrieval object so // that the hosting memory views can clean up. See 255120 and // 283586 DebugPlugin.getDefault().fireDebugEventSet( new DebugEvent[] { new DebugEvent(fMemRetrieval, DebugEvent.TERMINATE) }); // 'fireTerminate()' removes this launch from the list of 'DebugEvent' // listeners. The launch may not be terminated at this point: the inferior // and gdb processes are monitored in separate threads. This will prevent // updating of some of the Debug view actions. // 'DebugEvent.TERMINATE' will be fired when each of the corresponding processes // exits and handled by 'handleDebugEvents()' method. if (isTerminated()) fireTerminate(); rm.setStatus(getStatus()); rm.done(); } }); fExecutor.execute(shutdownSeq); } @SuppressWarnings("rawtypes") @Override public Object getAdapter(Class adapter) { // Must force adapters to be loaded. Platform.getAdapterManager().loadAdapter(this, adapter.getName()); return super.getAdapter(adapter); } @Override public void launchRemoved(ILaunch launch) { if (this.equals(launch)) { fExecutor.shutdown(); fExecutor = null; } super.launchRemoved(launch); } }