From b38273c84d367817f359ac12c2065872ea53bd45 Mon Sep 17 00:00:00 2001 From: Mikhail Khodjaiants Date: Mon, 24 Nov 2014 14:53:58 -0500 Subject: [PATCH] Bug 434558 - Disconnect actually terminates the session if launch element is selected Change-Id: I7a0064b9d9ed316079f0da80834fef278701f568 Reviewed-on: https://git.eclipse.org/r/26318 Reviewed-by: Marc Khouzam Reviewed-by: Mikhail Khodjaiants Tested-by: Mikhail Khodjaiants --- .../ui/actions/DsfTerminateCommand.java | 349 +++++++++++------- .../ui/actions/GdbDisconnectCommand.java | 290 ++++++++++++--- .../dsf/gdb/internal/ui/actions/Messages.java | 26 ++ .../internal/ui/actions/Messages.properties | 12 + .../cdt/dsf/gdb/launching/GdbLaunch.java | 18 +- .../cdt/dsf/gdb/service/GDBProcesses_7_2.java | 181 ++++++++- dsf/org.eclipse.cdt.dsf/META-INF/MANIFEST.MF | 2 +- dsf/org.eclipse.cdt.dsf/pom.xml | 2 +- .../cdt/dsf/debug/service/IMultiDetach.java | 55 +++ .../dsf/debug/service/IMultiTerminate.java | 49 +++ 10 files changed, 799 insertions(+), 185 deletions(-) create mode 100644 dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/actions/Messages.java create mode 100644 dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/actions/Messages.properties create mode 100644 dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IMultiDetach.java create mode 100644 dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IMultiTerminate.java diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/actions/DsfTerminateCommand.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/actions/DsfTerminateCommand.java index 496e1d69462..4c3b7fa6822 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/actions/DsfTerminateCommand.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/actions/DsfTerminateCommand.java @@ -11,14 +11,16 @@ *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.internal.ui.actions; -import java.util.concurrent.RejectedExecutionException; +import java.util.HashSet; +import java.util.Set; import java.util.concurrent.TimeUnit; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; import org.eclipse.cdt.dsf.concurrent.DsfExecutor; import org.eclipse.cdt.dsf.concurrent.DsfRunnable; -import org.eclipse.cdt.dsf.concurrent.ImmediateDataRequestMonitor; -import org.eclipse.cdt.dsf.concurrent.ImmediateRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.debug.service.IMultiTerminate; import org.eclipse.cdt.dsf.debug.service.IProcesses; import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext; import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin; @@ -28,10 +30,16 @@ import org.eclipse.cdt.dsf.service.DsfServicesTracker; import org.eclipse.cdt.dsf.service.DsfSession; import org.eclipse.cdt.dsf.service.DsfSession.SessionEndedListener; import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.MultiStatus; import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.commands.IDebugCommandRequest; import org.eclipse.debug.core.commands.IEnabledStateRequest; import org.eclipse.debug.core.commands.ITerminateHandler; +import org.eclipse.debug.core.model.IProcess; public class DsfTerminateCommand implements ITerminateHandler { private final DsfSession fSession; @@ -50,148 +58,112 @@ public class DsfTerminateCommand implements ITerminateHandler { @Override public void canExecute(final IEnabledStateRequest request) { - if (request.getElements().length != 1 || - !(request.getElements()[0] instanceof IDMVMContext || - request.getElements()[0] instanceof GdbLaunch)) { + if (request.getElements().length == 0) { request.setEnabled(false); request.done(); return; } - if (request.getElements()[0] instanceof GdbLaunch) { - canExecute(((GdbLaunch)request.getElements()[0]), request); - return; - } - - IDMVMContext vmc = (IDMVMContext)request.getElements()[0]; - - // First check if there is an ancestor process to terminate. This is the smallest entity we can terminate - final IProcessDMContext processDmc = DMContexts.getAncestorOfType(vmc.getDMContext(), IProcessDMContext.class); - if (processDmc == null) { - request.setEnabled(false); - request.done(); - return; - } - - canExecute(processDmc, request); + final GdbLaunch launch = getLaunch(request); + if (launch != null) { + fExecutor.execute(new DsfRunnable() { + @Override + public void run() { + request.setEnabled(false); + IGDBControl gdbControl = fTracker.getService(IGDBControl.class); + if (gdbControl != null && gdbControl.isActive()) { + request.setEnabled(true); + } + else { + // The GDB session may be terminated at this moment but if there + // are processes in this launch that are not controlled by GDB + // we need to check them as well. + for (IProcess p : launch.getProcesses()) { + if (p.canTerminate()) { + request.setEnabled(true); + break; + } + } + } + request.done(); + } + }); + } + else { + fExecutor.execute(new DsfRunnable() { + @Override + public void run() { + IProcessDMContext[] procDmcs = getProcessDMContexts(request.getElements()); + canTerminate(procDmcs, new DataRequestMonitor(fExecutor, null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + request.setEnabled(false); + } + else { + request.setEnabled(getData()); + } + request.done(); + } + }); + } + }); + } } @Override public boolean execute(final IDebugCommandRequest request) { - if (request.getElements().length != 1 || - !(request.getElements()[0] instanceof IDMVMContext || - request.getElements()[0] instanceof GdbLaunch)) { + if (request.getElements().length == 0) { request.done(); return false; } - if (request.getElements()[0] instanceof GdbLaunch) { - return execute(((GdbLaunch)request.getElements()[0]), request); - } - - IDMVMContext vmc = (IDMVMContext)request.getElements()[0]; - - // First check if there is an ancestor process to terminate. This is the smallest entity we can terminate - final IProcessDMContext processDmc = DMContexts.getAncestorOfType(vmc.getDMContext(), IProcessDMContext.class); - if (processDmc == null) { - request.done(); - return false; - } - - return execute(processDmc, request); - } - - private void canExecute(GdbLaunch launch, IEnabledStateRequest request) { - request.setEnabled(launch.canTerminate()); - request.done(); - } - - private boolean execute(GdbLaunch launch, final IDebugCommandRequest request) { - try { - fExecutor.execute(new DsfRunnable() { - @Override - public void run() { - final IGDBControl commandControl = fTracker.getService(IGDBControl.class); - if (commandControl != null) { - commandControl.terminate(new ImmediateRequestMonitor() { - @Override - protected void handleCompleted() { - if (!isSuccess()) { - request.setStatus(getStatus()); - request.done(); - } - else { - waitForTermination(request); - } - }; - }); - } else { - request.done(); - } - } - }); - } catch (RejectedExecutionException e) { - request.done(); - } - return false; - } - - private void canExecute(final IProcessDMContext processDmc, final IEnabledStateRequest request) { - try { - fExecutor.execute( - new DsfRunnable() { - @Override - public void run() { - // Get the processes service and the exec context. - IProcesses procService = fTracker.getService(IProcesses.class); - if (procService == null) { - // Service already invalid. - request.setEnabled(false); - request.done(); - } else { - procService.canTerminate(processDmc, new ImmediateDataRequestMonitor() { - @Override - protected void handleCompleted() { - request.setEnabled(isSuccess() && getData()); - request.done(); - } - }); - } - } - }); - } catch (RejectedExecutionException e) { - request.setEnabled(false); - request.done(); - } - } - - private boolean execute(final IProcessDMContext processDmc, final IDebugCommandRequest request) { - try { - fExecutor.execute(new DsfRunnable() { - @Override - public void run() { - IProcesses procService = fTracker.getService(IProcesses.class); - if (procService != null) { - procService.terminate(processDmc, new ImmediateRequestMonitor() { - @Override - protected void handleCompleted() { - if (!isSuccess()) { - request.setStatus(getStatus()); - request.done(); - } - else { - waitForTermination(request); - } - }; - }); - } else { - request.done(); - } - } - }); - } catch (RejectedExecutionException e) { - request.done(); + final GdbLaunch launch = getLaunch(request); + if (launch != null) { + fExecutor.execute(new DsfRunnable() { + @Override + public void run() { + IGDBControl gdbControl = fTracker.getService(IGDBControl.class); + if (gdbControl != null && gdbControl.isActive()) { + gdbControl.terminate(new RequestMonitor(fExecutor, null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + request.setStatus(getStatus()); + request.done(); + } + else { + waitForTermination(request); + } + } + }); + } + else { + terminateRemainingProcesses(launch, request); + } + } + }); } + else { + fExecutor.execute(new DsfRunnable() { + @Override + public void run() { + IProcessDMContext[] procDmcs = getProcessDMContexts(request.getElements()); + terminate(procDmcs, new RequestMonitor(fExecutor, null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + request.setStatus(getStatus()); + request.done(); + } + else { + waitForTermination(request); + } + } + }); + } + }); + } return false; } @@ -217,7 +189,13 @@ public class DsfTerminateCommand implements ITerminateHandler { public void sessionEnded(DsfSession session) { if (fSession.equals(session)) { DsfSession.removeSessionEndedListener(this); - request.done(); + GdbLaunch launch = getLaunch(request); + if (launch != null) { + terminateRemainingProcesses(launch, request); + } + else { + request.done(); + } } } }; @@ -254,4 +232,111 @@ public class DsfTerminateCommand implements ITerminateHandler { }}, 1, TimeUnit.MINUTES); } + + private IProcessDMContext[] getProcessDMContexts(Object[] elements) { + final Set procDmcs = new HashSet(); + for (Object obj : elements) { + if (obj instanceof IDMVMContext) { + IProcessDMContext procDmc = + DMContexts.getAncestorOfType(((IDMVMContext)obj).getDMContext(), IProcessDMContext.class); + if (procDmc != null) { + procDmcs.add(procDmc); + } + } + } + return procDmcs.toArray(new IProcessDMContext[procDmcs.size()]); + } + + private void canTerminate(IProcessDMContext[] procDmcs, DataRequestMonitor rm) { + if (procDmcs.length == 0) { + IGDBControl gdbControl = fTracker.getService(IGDBControl.class); + if (gdbControl != null) { + rm.setData(gdbControl.isActive()); + } + else { + rm.setStatus(new Status(IStatus.ERROR, GdbUIPlugin.PLUGIN_ID, "Service is not available.")); //$NON-NLS-1$ + } + rm.done(); + return; + } + + IMultiTerminate multiTerminate = fTracker.getService(IMultiTerminate.class); + if (multiTerminate != null) { + multiTerminate.canTerminateSome(procDmcs, rm); + } + else { + IProcesses procService = fTracker.getService(IProcesses.class); + if (procService != null && procDmcs.length == 1) { + procService.canTerminate(procDmcs[0], rm); + } + else { + rm.setData(false); + rm.done(); + } + } + } + + private void terminate(IProcessDMContext[] procDmcs, RequestMonitor rm) { + if (procDmcs.length == 0) { + IGDBControl gdbControl = fTracker.getService(IGDBControl.class); + if (gdbControl != null) { + gdbControl.terminate(rm); + } + else { + rm.done(); + } + return; + } + + IMultiTerminate multiTerminate = fTracker.getService(IMultiTerminate.class); + if (multiTerminate != null) { + multiTerminate.terminate(procDmcs, rm); + } + else { + IProcesses procService = fTracker.getService(IProcesses.class); + if (procService != null && procDmcs.length == 1) { + procService.terminate(procDmcs[0], rm); + } + else { + rm.done(); + } + } + } + + private GdbLaunch getLaunch(IDebugCommandRequest request) { + for (Object el : request.getElements()) { + if (el instanceof GdbLaunch) { + return (GdbLaunch)el; + } + } + return null; + } + + private void terminateRemainingProcesses(final GdbLaunch launch, final IDebugCommandRequest request) { + // Run this in a separate job since this method is called from + // the executor thread. The job is scheduled with a delay to make + // sure that MIInferiorProcess is terminated. See MIInferiorProcess.waitForSync() + new Job("Terminate Job") { //$NON-NLS-1$ + @Override + protected IStatus run(IProgressMonitor monitor) { + MultiStatus status = + new MultiStatus(GdbUIPlugin.PLUGIN_ID, DebugException.REQUEST_FAILED, Messages.DsfTerminateCommand_Terminate_failed, null); + for (IProcess p : launch.getProcesses()) { + if (p.canTerminate()) { + try { + p.terminate(); + } + catch(DebugException e) { + status.merge(e.getStatus()); + } + } + } + if (!status.isOK()) { + request.setStatus(status); + } + request.done(); + return Status.OK_STATUS; + } + }.schedule(100); + } } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/actions/GdbDisconnectCommand.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/actions/GdbDisconnectCommand.java index 93c56084d43..065790365d5 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/actions/GdbDisconnectCommand.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/actions/GdbDisconnectCommand.java @@ -10,81 +10,281 @@ *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.internal.ui.actions; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.TimeUnit; + import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.concurrent.ImmediateDataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.ImmediateRequestMonitor; 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.IMultiDetach; import org.eclipse.cdt.dsf.debug.service.IProcesses; import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; -import org.eclipse.cdt.dsf.debug.ui.actions.DsfCommandRunnable; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin; +import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch; import org.eclipse.cdt.dsf.service.DsfServicesTracker; import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.service.DsfSession.SessionEndedListener; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; import org.eclipse.debug.core.commands.IDebugCommandRequest; import org.eclipse.debug.core.commands.IDisconnectHandler; import org.eclipse.debug.core.commands.IEnabledStateRequest; public class GdbDisconnectCommand implements IDisconnectHandler { - private final DsfExecutor fExecutor; + private final DsfSession fSession; + private final DsfExecutor fExecutor; private final DsfServicesTracker fTracker; - - public GdbDisconnectCommand(DsfSession session) { - fExecutor = session.getExecutor(); + + public GdbDisconnectCommand(DsfSession session) { + super(); + fSession = session; + fExecutor = session.getExecutor(); fTracker = new DsfServicesTracker(GdbUIPlugin.getBundleContext(), session.getId()); - } + } public void dispose() { fTracker.dispose(); } - @Override - public void canExecute(final IEnabledStateRequest request) { - if (request.getElements().length != 1) { + @Override + public void canExecute(final IEnabledStateRequest request) { + if (request.getElements().length == 0) { request.setEnabled(false); request.done(); return; } - fExecutor.submit(new DsfCommandRunnable(fTracker, request.getElements()[0], request) { - @Override public void doExecute() { - IContainerDMContext containerDmc = DMContexts.getAncestorOfType(getContext(), IContainerDMContext.class); - IProcesses procService = getProcessService(); - - if (procService != null) { - procService.canDetachDebuggerFromProcess( - containerDmc, - new DataRequestMonitor(fExecutor, null) { - @Override - protected void handleCompleted() { - request.setEnabled(isSuccess() && getData()); - request.done(); - } - }); - } else { - request.setEnabled(false); - request.done(); - } - } + getContainerDMContexts(request.getElements(), new DataRequestMonitor(fExecutor, null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + request.setEnabled(false); + request.done(); + } + else { + canDisconnect(getData(), new ImmediateDataRequestMonitor() { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + request.setEnabled(false); + } + else { + request.setEnabled(getData());; + } + request.done(); + } + }); + } + } }); } - @Override + @Override public boolean execute(final IDebugCommandRequest request) { - if (request.getElements().length != 1) { - request.done(); - return false; + if (request.getElements().length == 0) { + request.done(); + return false; } - - fExecutor.submit(new DsfCommandRunnable(fTracker, request.getElements()[0], request) { - @Override public void doExecute() { - IContainerDMContext containerDmc = DMContexts.getAncestorOfType(getContext(), IContainerDMContext.class); - IProcesses procService = getProcessService(); - - if (procService != null) { - procService.detachDebuggerFromProcess(containerDmc, new RequestMonitor(fExecutor, null)); - } - } + + getContainerDMContexts(request.getElements(), new DataRequestMonitor(fExecutor, null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + request.setStatus(getStatus()); + request.done(); + } + else { + disconnect(getData(), new ImmediateRequestMonitor() { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + request.setStatus(getStatus()); + request.done(); + } + else { + waitForTermination(request); + } + } + }); + } + } }); - return false; - } + + return false; + } + + /** + * Wait for the debug session to be fully shutdown before reporting + * that the terminate was completed. This is important for the + * 'Terminate and remove' operation. + * The wait time is limited with a timeout so as to eventually complete the + * request in the case of termination error, or when terminating + * a single process in a multi-process session. + * See bug 377447 + */ + private void waitForTermination(final IDebugCommandRequest request) { + // It is possible that the session already had time to terminate + if (!DsfSession.isSessionActive(fSession.getId())) { + request.done(); + return; + } + + // Listener that will indicate when the shutdown is complete + final SessionEndedListener endedListener = new SessionEndedListener () { + @Override + public void sessionEnded(DsfSession session) { + if (fSession.equals(session)) { + DsfSession.removeSessionEndedListener(this); + request.done(); + } + } + }; + + DsfSession.addSessionEndedListener(endedListener); + + // Create the timeout + // For a multi-process session, if a single process is + // terminated, this timeout will always hit (unless the + // session is also terminated before the timeout). + // We haven't found a problem with delaying the completion + // of the request that way. + // Note that this timeout is not removed even if we don't + // need it anymore, once the session has terminated; + // instead, we let it timeout and ignore it if the session + // is already terminated. + fExecutor.schedule(new Runnable() { + @Override + public void run() { + // Check that the session is still active when the timeout hits. + // If it is not, then everything has been cleaned up already. + if (DsfSession.isSessionActive(fSession.getId())) { + DsfSession.removeSessionEndedListener(endedListener); + + // Marking the request as cancelled will prevent the removal of + // the launch from the Debug view in case of "Terminate and Remove". + // This is important for multi-process sessions when "Terminate and Remove" + // is applied to one of the running processes. In this case the selected + // process will be terminated but the associated launch will not be removed + // from the Debug view. + request.setStatus(Status.CANCEL_STATUS); + request.done(); + } + }}, + 1, TimeUnit.MINUTES); + } + + private void getContainerDMContexts(Object[] elements, final DataRequestMonitor rm) { + GdbLaunch launch = null; + final Set contDmcs = new HashSet(); + for (Object obj : elements) { + if (obj instanceof GdbLaunch) { + launch = (GdbLaunch)obj; + break; + } + if (obj instanceof IDMVMContext) { + IContainerDMContext contDmc = + DMContexts.getAncestorOfType(((IDMVMContext)obj).getDMContext(), IContainerDMContext.class); + if (contDmc != null) { + contDmcs.add(contDmc); + } + } + } + if (launch == null) { + rm.setData(contDmcs.toArray(new IContainerDMContext[contDmcs.size()])); + rm.done(); + } + else { + try { + fExecutor.execute(new DsfRunnable() { + @Override + public void run() { + ICommandControlService commandControl = fTracker.getService(ICommandControlService.class); + final IProcesses procService = fTracker.getService(IProcesses.class); + if (commandControl != null && procService != null) { + procService.getProcessesBeingDebugged( + commandControl.getContext(), + new ImmediateDataRequestMonitor() { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + rm.setStatus(getStatus()); + } + else { + for (IDMContext ctx : getData()) { + IContainerDMContext contDmc = DMContexts.getAncestorOfType(ctx, IContainerDMContext.class); + if (contDmc != null) { + contDmcs.add(contDmc); + } + } + rm.setData(contDmcs.toArray(new IContainerDMContext[contDmcs.size()])); + } + rm.done(); + }; + }); + } + else { + rm.setStatus(new Status(IStatus.ERROR, GdbUIPlugin.PLUGIN_ID, "Service is not available.")); //$NON-NLS-1$ + rm.done(); + } + } + }); + } catch (RejectedExecutionException e) { + rm.setStatus(new Status(IStatus.ERROR, GdbUIPlugin.PLUGIN_ID, e.getLocalizedMessage())); + rm.done(); + } + } + } + + private void canDisconnect(IContainerDMContext[] contDmcs, DataRequestMonitor rm) { + if (contDmcs.length == 0) { + rm.setData(false); + rm.done(); + return; + } + + IMultiDetach multiDetach = fTracker.getService(IMultiDetach.class); + if (multiDetach != null) { + multiDetach.canDetachDebuggerFromSomeProcesses(contDmcs, rm); + } + else { + IProcesses procService = fTracker.getService(IProcesses.class); + if (procService != null && contDmcs.length == 1) { + procService.canDetachDebuggerFromProcess(contDmcs[0], rm); + } + else { + rm.setData(false); + rm.done(); + } + } + } + + private void disconnect(IContainerDMContext[] contDmcs, RequestMonitor rm) { + if (contDmcs.length == 0) { + rm.done(); + return; + } + + IMultiDetach multiDetach = fTracker.getService(IMultiDetach.class); + if (multiDetach != null) { + multiDetach.detachDebuggerFromProcesses(contDmcs, rm); + } + else { + IProcesses procService = fTracker.getService(IProcesses.class); + if (procService != null && contDmcs.length == 1) { + procService.detachDebuggerFromProcess(contDmcs[0], rm); + } + else { + rm.done(); + } + } + } } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/actions/Messages.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/actions/Messages.java new file mode 100644 index 00000000000..f659752a166 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/actions/Messages.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2014 Mentor Graphics 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: + * Mentor Graphics - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.gdb.internal.ui.actions; + +import org.eclipse.osgi.util.NLS; + +public class Messages extends NLS { + public static String DsfTerminateCommand_Terminate_failed; + + static { + // initialize resource bundle + NLS.initializeMessages(Messages.class.getName(), Messages.class); + } + + private Messages() { + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/actions/Messages.properties b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/actions/Messages.properties new file mode 100644 index 00000000000..440eaa1f643 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/actions/Messages.properties @@ -0,0 +1,12 @@ +############################################################################### +# Copyright (c) 2014 Mentor Graphics 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: +# Mentor Graphics - Initial API and implementation +############################################################################### + +DsfTerminateCommand_Terminate_failed=Terminate failed diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GdbLaunch.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GdbLaunch.java index 9122a44ad3d..540519f07e8 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GdbLaunch.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GdbLaunch.java @@ -57,6 +57,7 @@ import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.IStatusHandler; import org.eclipse.debug.core.commands.IDebugCommandRequest; +import org.eclipse.debug.core.commands.IDisconnectHandler; import org.eclipse.debug.core.commands.ITerminateHandler; import org.eclipse.debug.core.model.IDisconnect; import org.eclipse.debug.core.model.ISourceLocator; @@ -195,10 +196,10 @@ public class GdbLaunch extends DsfLaunch /////////////////////////////////////////////////////////////////////////// // ITerminate - static class TerminateRequest extends CRequest implements IDebugCommandRequest { + static class LaunchCommandRequest extends CRequest implements IDebugCommandRequest { Object[] elements; - public TerminateRequest(Object[] objects) { + public LaunchCommandRequest(Object[] objects) { elements = objects; } @@ -244,7 +245,7 @@ public class GdbLaunch extends DsfLaunch return; } - TerminateRequest req = new TerminateRequest(new Object[] {this}); + LaunchCommandRequest req = new LaunchCommandRequest(new Object[] {this}); handler.execute(req); } @@ -265,7 +266,14 @@ public class GdbLaunch extends DsfLaunch @Override public void disconnect() throws DebugException { - terminate(); + IDisconnectHandler handler = (IDisconnectHandler)getAdapter(IDisconnectHandler.class); + if (handler == null) { + super.disconnect(); + return; + } + + LaunchCommandRequest req = new LaunchCommandRequest(new Object[] {this}); + handler.execute(req); } // IDisconnect @@ -362,6 +370,8 @@ public class GdbLaunch extends DsfLaunch // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=377447. if (adapter.equals(ITerminateHandler.class)) return getSession().getModelAdapter(adapter); + if (adapter.equals(IDisconnectHandler.class)) + return getSession().getModelAdapter(adapter); // Allow to call the connect handler when the launch is selected if (adapter.equals(IConnectHandler.class)) diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses_7_2.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses_7_2.java index 3f1efde95a2..716e2c3a555 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses_7_2.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses_7_2.java @@ -12,11 +12,15 @@ *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.service; +import java.util.Arrays; import java.util.HashSet; +import java.util.Hashtable; +import java.util.Iterator; import java.util.Map; import java.util.Set; import org.eclipse.cdt.debug.core.CDebugUtils; +import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor; import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; import org.eclipse.cdt.dsf.concurrent.DsfExecutor; import org.eclipse.cdt.dsf.concurrent.ImmediateDataRequestMonitor; @@ -28,6 +32,8 @@ 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.IMemory.IMemoryDMContext; +import org.eclipse.cdt.dsf.debug.service.IMultiDetach; +import org.eclipse.cdt.dsf.debug.service.IMultiTerminate; import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; import org.eclipse.cdt.dsf.debug.service.IRunControl.IExitedDMEvent; import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; @@ -58,9 +64,71 @@ import org.eclipse.debug.core.ILaunch; * * @since 4.0 */ -public class GDBProcesses_7_2 extends GDBProcesses_7_1 { +public class GDBProcesses_7_2 extends GDBProcesses_7_1 implements IMultiTerminate, IMultiDetach { + + abstract private class ConditionalRequestMonitor extends ImmediateDataRequestMonitor { + + private Iterator fIterator; + private boolean fAll = true; + private DataRequestMonitor fParentMonitor; + + private ConditionalRequestMonitor(Iterator it, boolean all, DataRequestMonitor parentMonitor) { + super(parentMonitor); + fAll = all; + fParentMonitor = parentMonitor; + fIterator = it; + } + + @Override + protected void handleCompleted() { + if (!isSuccess()) { + fParentMonitor.setStatus(getStatus()); + fParentMonitor.done(); + return; + } + + if (getData() != fAll) { + fParentMonitor.setData(getData()); + fParentMonitor.done(); + } + else if (!fIterator.hasNext()) { + fParentMonitor.setData(fAll); + fParentMonitor.done(); + } + else { + proceed(fIterator, fAll, fParentMonitor); + } + } + + abstract protected void proceed(Iterator it, boolean all, DataRequestMonitor parentMonitor); + } - /** + private class CanDetachRequestMonitor extends ConditionalRequestMonitor { + + private CanDetachRequestMonitor(Iterator it, boolean all, DataRequestMonitor parentMonitor) { + super(it, all, parentMonitor); + } + + @Override + protected void proceed(Iterator it, boolean all, DataRequestMonitor parentMonitor) { + canDetachDebuggerFromProcess(it.next(), new CanDetachRequestMonitor(it, all, parentMonitor)); + } + + } + + private class CanTerminateRequestMonitor extends ConditionalRequestMonitor { + + private CanTerminateRequestMonitor(Iterator it, boolean all, DataRequestMonitor parentMonitor) { + super(it, all, parentMonitor); + } + + @Override + protected void proceed(Iterator it, boolean all, DataRequestMonitor parentMonitor) { + canTerminate((IThreadDMContext)it.next(), new CanTerminateRequestMonitor(it, all, parentMonitor)); + } + } + + /** * The id of the single thread to be used during event visualization. * @since 4.1 */ @@ -114,6 +182,8 @@ public class GDBProcesses_7_2 extends GDBProcesses_7_1 { * initialization is done. */ private void doInitialize(RequestMonitor requestMonitor) { + register(new String[]{ IMultiDetach.class.getName(), IMultiTerminate.class.getName() }, new Hashtable()); + fCommandControl = getServicesTracker().getService(IGDBControl.class); fCommandFactory = getServicesTracker().getService(IMICommandControl.class).getCommandFactory(); fBackend = getServicesTracker().getService(IGDBBackend.class); @@ -607,5 +677,112 @@ public class GDBProcesses_7_2 extends GDBProcesses_7_1 { public void eventDispatched(ITraceRecordSelectedChangedDMEvent e) { setTraceVisualization(e.isVisualizationModeEnabled()); } + + /** + * @since 4.6 + */ + @Override + public void canDetachDebuggerFromSomeProcesses(IDMContext[] dmcs, final DataRequestMonitor rm) { + canDetachFromProcesses(dmcs, false, rm); + } + + /** + * @since 4.6 + */ + @Override + public void canDetachDebuggerFromAllProcesses(IDMContext[] dmcs, DataRequestMonitor rm) { + canDetachFromProcesses(dmcs, true, rm); + } + + /** + * @since 4.6 + */ + protected void canDetachFromProcesses(IDMContext[] dmcs, boolean all, DataRequestMonitor rm) { + Set contDmcs = new HashSet(); + for (IDMContext c : dmcs) { + IMIContainerDMContext contDmc = DMContexts.getAncestorOfType(c, IMIContainerDMContext.class); + if (contDmc != null) { + contDmcs.add(contDmc); + } + } + + Iterator it = contDmcs.iterator(); + if (!it.hasNext()) { + rm.setData(false); + rm.done(); + return; + } + canDetachDebuggerFromProcess(it.next(), new CanDetachRequestMonitor(it, all, rm)); + } + + /** + * @since 4.6 + */ + @Override + public void detachDebuggerFromProcesses(IDMContext[] dmcs, final RequestMonitor rm) { + Set contDmcs = new HashSet(); + for (IDMContext c : dmcs) { + IMIContainerDMContext contDmc = DMContexts.getAncestorOfType(c, IMIContainerDMContext.class); + if (contDmc != null) { + contDmcs.add(contDmc); + } + } + if (contDmcs.isEmpty()) { + rm.done(); + return; + } + + CountingRequestMonitor crm = new CountingRequestMonitor(ImmediateExecutor.getInstance(), rm); + crm.setDoneCount(contDmcs.size()); + for (IMIContainerDMContext contDmc : contDmcs) { + detachDebuggerFromProcess(contDmc, crm); + } + } + + /** + * @since 4.6 + */ + @Override + public void canTerminateSome(IThreadDMContext[] dmcs, DataRequestMonitor rm) { + canTerminate(dmcs, false, rm); + } + + /** + * @since 4.6 + */ + @Override + public void canTerminateAll(IThreadDMContext[] dmcs, DataRequestMonitor rm) { + canTerminate(dmcs, true, rm); + } + + /** + * @since 4.6 + */ + protected void canTerminate(IThreadDMContext[] dmcs, boolean all, DataRequestMonitor rm) { + Iterator it = Arrays.asList(dmcs).iterator(); + if (!it.hasNext()) { + rm.setData(false); + rm.done(); + return; + } + canTerminate(it.next(), new CanTerminateRequestMonitor(it, all, rm)); + } + + /** + * @since 4.6 + */ + @Override + public void terminate(IThreadDMContext[] dmcs, RequestMonitor rm) { + if (dmcs.length == 0) { + rm.done(); + return; + } + + CountingRequestMonitor crm = new CountingRequestMonitor(ImmediateExecutor.getInstance(), rm); + crm.setDoneCount(dmcs.length); + for (IThreadDMContext threadDmc : dmcs) { + terminate(threadDmc, crm); + } + } } diff --git a/dsf/org.eclipse.cdt.dsf/META-INF/MANIFEST.MF b/dsf/org.eclipse.cdt.dsf/META-INF/MANIFEST.MF index d3f45011852..d4a438d61c4 100644 --- a/dsf/org.eclipse.cdt.dsf/META-INF/MANIFEST.MF +++ b/dsf/org.eclipse.cdt.dsf/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-Vendor: %providerName Bundle-SymbolicName: org.eclipse.cdt.dsf;singleton:=true -Bundle-Version: 2.5.0.qualifier +Bundle-Version: 2.6.0.qualifier Bundle-Activator: org.eclipse.cdt.dsf.internal.DsfPlugin Bundle-Localization: plugin Require-Bundle: org.eclipse.core.runtime, diff --git a/dsf/org.eclipse.cdt.dsf/pom.xml b/dsf/org.eclipse.cdt.dsf/pom.xml index 675280943d6..a98b5dc2403 100644 --- a/dsf/org.eclipse.cdt.dsf/pom.xml +++ b/dsf/org.eclipse.cdt.dsf/pom.xml @@ -11,7 +11,7 @@ ../../pom.xml - 2.5.0-SNAPSHOT + 2.6.0-SNAPSHOT org.eclipse.cdt.dsf eclipse-plugin diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IMultiDetach.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IMultiDetach.java new file mode 100644 index 00000000000..cb0744096dd --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IMultiDetach.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2014 Mentor Graphics 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: + * Mentor Graphics - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.debug.service; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; + +/** + * This interface provides the ability to perform detach on multiple contexts. + * + * @since 2.6 + */ +public interface IMultiDetach { + + /** + * Checks whether it is possible to detach the debugger from at least one + * of the specified processes. + * + * @param dmcs The contexts to detach the debugger from. Each context + * should have {@link IContainerDMContext} as an ancestor. + * @param rm Request monitor returning whether there is at least one context + * that can be detached from the debugger. + */ + void canDetachDebuggerFromSomeProcesses(IDMContext[] dmcs, DataRequestMonitor rm); + + /** + * Checks whether it is possible to detach the debugger from all of the specified processes. + * + * @param dmc The contexts to detach the debugger from. Each context + * should have {@link IContainerDMContext} as an ancestor. + * @param rm Request monitor returning whether all processes specified by the given contexts + * that can be detached from the debugger. + */ + void canDetachDebuggerFromAllProcesses(IDMContext[] dmcs, DataRequestMonitor rm); + + /** + * Request to detach debugger from the specified processes. Only contexts + * that are in a state that can be detached will be affected, others will be ignored. + * + * @param dmc The contexts to detach the debugger from. Each context + * should have {@link IContainerDMContext} as an ancestor. + */ + void detachDebuggerFromProcesses(IDMContext[] dmcs, RequestMonitor rm); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IMultiTerminate.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IMultiTerminate.java new file mode 100644 index 00000000000..20e20d1d3fe --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IMultiTerminate.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2014 Mentor Graphics 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: + * Mentor Graphics - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.debug.service; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.debug.service.IProcesses.IThreadDMContext; + +/** + * This interface provides the ability to perform terminate on multiple contexts. + * + * @since 2.6 + */ +public interface IMultiTerminate { + + /** + * Checks whether it is possible to terminate at least one of the specified threads + * or processes. + * + * @param dmcs The contexts of the threads to terminate + * @param rm Request monitor returning whether there is at least one thread or process can be terminated. + */ + void canTerminateSome(IThreadDMContext[] dmcs, DataRequestMonitor rm); + + /** + * Checks whether it is possible to terminate all of the specified threads or processes. + * + * @param dmcs The contexts of the threads or processes to terminate + * @param rm Request monitor returning whether all of the threads can be terminated. + */ + void canTerminateAll(IThreadDMContext[] dmcs, DataRequestMonitor rm); + + /** + * Request to terminate the specified threads or processes. Only threads and processes + * that can be terminated will be affected, others will be ignored. + * + * @param dmc The contexts of the threads or processes to terminate. + */ + void terminate(IThreadDMContext[] dmcs, RequestMonitor rm); +} \ No newline at end of file