1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-06-08 02:06:01 +02:00

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 <marc.khouzam@ericsson.com>
Reviewed-by: Mikhail Khodjaiants <mikhailkhod@googlemail.com>
Tested-by: Mikhail Khodjaiants <mikhailkhod@googlemail.com>
This commit is contained in:
Mikhail Khodjaiants 2014-11-24 14:53:58 -05:00
parent 863def95be
commit b38273c84d
10 changed files with 799 additions and 185 deletions

View file

@ -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<Boolean>(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<Boolean>() {
@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<IProcessDMContext> procDmcs = new HashSet<IProcessDMContext>();
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<Boolean> 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);
}
}

View file

@ -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<Boolean>(fExecutor, null) {
@Override
protected void handleCompleted() {
request.setEnabled(isSuccess() && getData());
request.done();
}
});
} else {
request.setEnabled(false);
request.done();
}
}
getContainerDMContexts(request.getElements(), new DataRequestMonitor<IContainerDMContext[]>(fExecutor, null) {
@Override
protected void handleCompleted() {
if (!isSuccess()) {
request.setEnabled(false);
request.done();
}
else {
canDisconnect(getData(), new ImmediateDataRequestMonitor<Boolean>() {
@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<IContainerDMContext[]>(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<IContainerDMContext[]> rm) {
GdbLaunch launch = null;
final Set<IContainerDMContext> contDmcs = new HashSet<IContainerDMContext>();
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<IDMContext[]>() {
@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<Boolean> 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();
}
}
}
}

View file

@ -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() {
}
}

View file

@ -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

View file

@ -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))

View file

@ -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<Boolean> {
private Iterator<? extends IDMContext> fIterator;
private boolean fAll = true;
private DataRequestMonitor<Boolean> fParentMonitor;
private ConditionalRequestMonitor(Iterator<? extends IDMContext> it, boolean all, DataRequestMonitor<Boolean> 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<? extends IDMContext> it, boolean all, DataRequestMonitor<Boolean> parentMonitor);
}
/**
private class CanDetachRequestMonitor extends ConditionalRequestMonitor {
private CanDetachRequestMonitor(Iterator<? extends IDMContext> it, boolean all, DataRequestMonitor<Boolean> parentMonitor) {
super(it, all, parentMonitor);
}
@Override
protected void proceed(Iterator<? extends IDMContext> it, boolean all, DataRequestMonitor<Boolean> parentMonitor) {
canDetachDebuggerFromProcess(it.next(), new CanDetachRequestMonitor(it, all, parentMonitor));
}
}
private class CanTerminateRequestMonitor extends ConditionalRequestMonitor {
private CanTerminateRequestMonitor(Iterator<? extends IDMContext> it, boolean all, DataRequestMonitor<Boolean> parentMonitor) {
super(it, all, parentMonitor);
}
@Override
protected void proceed(Iterator<? extends IDMContext> it, boolean all, DataRequestMonitor<Boolean> 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<String,String>());
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<Boolean> rm) {
canDetachFromProcesses(dmcs, false, rm);
}
/**
* @since 4.6
*/
@Override
public void canDetachDebuggerFromAllProcesses(IDMContext[] dmcs, DataRequestMonitor<Boolean> rm) {
canDetachFromProcesses(dmcs, true, rm);
}
/**
* @since 4.6
*/
protected void canDetachFromProcesses(IDMContext[] dmcs, boolean all, DataRequestMonitor<Boolean> rm) {
Set<IMIContainerDMContext> contDmcs = new HashSet<IMIContainerDMContext>();
for (IDMContext c : dmcs) {
IMIContainerDMContext contDmc = DMContexts.getAncestorOfType(c, IMIContainerDMContext.class);
if (contDmc != null) {
contDmcs.add(contDmc);
}
}
Iterator<IMIContainerDMContext> 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<IMIContainerDMContext> contDmcs = new HashSet<IMIContainerDMContext>();
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<Boolean> rm) {
canTerminate(dmcs, false, rm);
}
/**
* @since 4.6
*/
@Override
public void canTerminateAll(IThreadDMContext[] dmcs, DataRequestMonitor<Boolean> rm) {
canTerminate(dmcs, true, rm);
}
/**
* @since 4.6
*/
protected void canTerminate(IThreadDMContext[] dmcs, boolean all, DataRequestMonitor<Boolean> rm) {
Iterator<IThreadDMContext> 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);
}
}
}

View file

@ -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,

View file

@ -11,7 +11,7 @@
<relativePath>../../pom.xml</relativePath>
</parent>
<version>2.5.0-SNAPSHOT</version>
<version>2.6.0-SNAPSHOT</version>
<artifactId>org.eclipse.cdt.dsf</artifactId>
<packaging>eclipse-plugin</packaging>
</project>

View file

@ -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<Boolean> 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<Boolean> 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);
}

View file

@ -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<Boolean> 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<Boolean> 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);
}