diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/commands/GdbConnectCommand.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/commands/GdbConnectCommand.java index ac2e2b94558..acb95c231e8 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/commands/GdbConnectCommand.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/commands/GdbConnectCommand.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008, 2015 Ericsson and others. + * Copyright (c) 2008, 2016 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 @@ -14,10 +14,12 @@ package org.eclipse.cdt.dsf.gdb.internal.ui.commands; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; import java.util.concurrent.RejectedExecutionException; import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; @@ -28,8 +30,11 @@ 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.ImmediateDataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor; +import org.eclipse.cdt.dsf.concurrent.ImmediateInDsfExecutor; import org.eclipse.cdt.dsf.concurrent.Query; 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.IProcesses; import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext; @@ -50,6 +55,7 @@ import org.eclipse.cdt.dsf.gdb.launching.LaunchMessages; import org.eclipse.cdt.dsf.gdb.service.IGDBBackend; import org.eclipse.cdt.dsf.gdb.service.IGDBProcesses; import org.eclipse.cdt.dsf.gdb.service.IGDBProcesses.IGdbThreadDMData; +import org.eclipse.cdt.dsf.mi.service.IMIProcessDMContext; import org.eclipse.cdt.dsf.gdb.service.SessionType; import org.eclipse.cdt.dsf.service.DsfServicesTracker; import org.eclipse.cdt.dsf.service.DsfSession; @@ -149,13 +155,15 @@ public class GdbConnectCommand extends RefreshableDebugCommand implements IConne DataRequestMonitor fRequestMonitor; boolean fNewProcessSupported; boolean fRemote; + private List fDebuggedProcesses; - public PromptForPidJob(String name, boolean newProcessSupported, boolean remote, IProcessExtendedInfo[] procs, DataRequestMonitor rm) { + public PromptForPidJob(String name, boolean newProcessSupported, boolean remote, IProcessExtendedInfo[] procs, List debuggedProcesses, DataRequestMonitor rm) { super(name); fNewProcessSupported = newProcessSupported; fRemote = remote; fProcessList = procs; fRequestMonitor = rm; + fDebuggedProcesses = debuggedProcesses; } @Override @@ -165,7 +173,7 @@ public class GdbConnectCommand extends RefreshableDebugCommand implements IConne null); try { - PrompterInfo info = new PrompterInfo(fNewProcessSupported, fRemote, fProcessList); + PrompterInfo info = new PrompterInfo(fNewProcessSupported, fRemote, fProcessList, fDebuggedProcesses); Object result = new ProcessPrompter().handleStatus(null, info); if (result == null) { fRequestMonitor.cancel(); @@ -278,12 +286,87 @@ public class GdbConnectCommand extends RefreshableDebugCommand implements IConne // Nothing to do, just ignore the command since the user // cancelled it. } catch (RejectedExecutionException e) { - // Can be thrown if the session is shutdown + // Can be thrown if the session is shutdown } finally { updateEnablement(); } } - + + /** + * Get already debugged processes from all compatible sessions. + * "compatible" in current implementation means all sessions on local machine. + * + * @param currentCtx current session context + * @param allSessions true if all session to be queried, false to return result only for current execution session context + * @param drm where result to be returned + */ + private void getAllDebuggedProcesses(final IDMContext currentCtx, boolean allSessions, final DataRequestMonitor> drm) { + SessionType sessionType = fTracker.getService(IGDBBackend.class).getSessionType(); + + final List result = new LinkedList<>(); + final List sessions = new LinkedList<>(); + // Only for local session types search in all debug sessions + if (allSessions && sessionType == SessionType.LOCAL) { + sessions.addAll(Arrays.asList(DsfSession.getActiveSessions())); + } else { + // For remote session just query current context. + // + // cannot reliably match two remote debug session that are connected to same target machine. + // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=486408#c7 + + sessions.add(DsfSession.getSession(currentCtx.getSessionId())); + } + + // Query each sessions for existing processes in a sequential fashion. + // We must do this as each session will require different executor. + final class ProcessRequestMonitor extends DataRequestMonitor { + public ProcessRequestMonitor(Executor executor) { super(executor, null);} + public ProcessRequestMonitor(DsfExecutor executor) { + super(new ImmediateInDsfExecutor(executor), null); + } + @Override + protected void handleCompleted() { + // if succeeded and has data, add process ids to result, + // otherwise proceed to next debug session (aka DsfSession) + if (isSuccess() && getData() != null) { + for (IDMContext dmc : getData()) { + IMIProcessDMContext procDmc = DMContexts.getAncestorOfType(dmc, + IMIProcessDMContext.class); + if (procDmc != null) { + result.add(procDmc.getProcId()); + } + } + } + if (!sessions.isEmpty()) { + final DsfSession nextSession = sessions.remove(0); + final boolean sameSession = currentCtx.getSessionId().equals(nextSession.getId()); + nextSession.getExecutor().execute(new DsfRunnable() { + @Override + public void run() { + DsfServicesTracker nextTracker = new DsfServicesTracker(GdbUIPlugin.getBundleContext(), nextSession.getId()); + IGDBBackend nextSessionBackend = nextTracker.getService(IGDBBackend.class); + if (sameSession || nextSessionBackend.getSessionType() == SessionType.LOCAL) { + ICommandControlService nextCommandControl = nextTracker.getService(ICommandControlService.class); + IProcesses nextProcService = nextTracker.getService(IProcesses.class); + nextProcService.getProcessesBeingDebugged( + nextCommandControl.getContext(), new ProcessRequestMonitor(nextSession.getExecutor())); + } else { + // proceed to next session context query passing an error (that will be ignored) + new ProcessRequestMonitor(nextSession.getExecutor()).done( + new Status(IStatus.ERROR, GdbUIPlugin.PLUGIN_ID, IDsfStatusConstants.NOT_SUPPORTED, "Only local session", null)); //$NON-NLS-1$ + } + nextTracker.dispose(); + } + }); + } else { + // done with querying all session. Copy the result + drm.done(result); + } + } + }; + // Trigger the first query + new ProcessRequestMonitor(ImmediateExecutor.getInstance()).done(); + } /* * This method should not be called from the UI thread. * (non-Javadoc) @@ -291,7 +374,7 @@ public class GdbConnectCommand extends RefreshableDebugCommand implements IConne */ @Override public void connect(final RequestMonitor rm) - { + { fExecutor.execute(new DsfRunnable() { @Override public void run() { @@ -318,35 +401,43 @@ public class GdbConnectCommand extends RefreshableDebugCommand implements IConne final List procInfoList = new ArrayList(); - final CountingRequestMonitor countingRm = + final CountingRequestMonitor countingRm = new CountingRequestMonitor(fExecutor, rm) { @Override protected void handleSuccess() { - // Prompt the user to choose one or more processes, or to start a new one - new PromptForPidJob( - LaunchUIMessages.getString("ProcessPrompter.PromptJob"), //$NON-NLS-1$ - newProcessSupported, - remote, - procInfoList.toArray(new IProcessExtendedInfo[procInfoList.size()]), - new DataRequestMonitor(fExecutor, rm) { - @Override - protected void handleCancel() { - rm.cancel(); - rm.done(); - } - @Override - protected void handleSuccess() { - Object data = getData(); - if (data instanceof NewExecutableInfo) { - // User wants to start a new process - startNewProcess(controlCtx, (NewExecutableInfo)data, rm); - } else if (data instanceof IProcessExtendedInfo[]) { - attachToProcesses(controlCtx, (IProcessExtendedInfo[])data, rm); - } else { - rm.done(new Status(IStatus.ERROR, GdbUIPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Invalid return type for process prompter", null)); //$NON-NLS-1$ - } - } - }).schedule(); + getAllDebuggedProcesses(controlCtx, true, new ImmediateDataRequestMonitor>(rm) { + @Override + protected void handleSuccess() { + List dbgPids = getData(); + + // Prompt the user to choose one or more processes, or to start a new one + new PromptForPidJob( + LaunchUIMessages.getString("ProcessPrompter.PromptJob"), //$NON-NLS-1$ + newProcessSupported, + remote, + procInfoList.toArray(new IProcessExtendedInfo[procInfoList.size()]), + dbgPids, + new DataRequestMonitor(fExecutor, rm) { + @Override + protected void handleCancel() { + rm.cancel(); + rm.done(); + } + @Override + protected void handleSuccess() { + Object data = getData(); + if (data instanceof NewExecutableInfo) { + // User wants to start a new process + startNewProcess(controlCtx, (NewExecutableInfo)data, rm); + } else if (data instanceof IProcessExtendedInfo[]) { + attachToProcesses(controlCtx, (IProcessExtendedInfo[])data, rm); + } else { + rm.done(new Status(IStatus.ERROR, GdbUIPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Invalid return type for process prompter", null)); //$NON-NLS-1$ + } + } + }).schedule(); + } + }); } }; diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/LaunchUIMessages.properties b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/LaunchUIMessages.properties index d2886cc9f45..03cf0d35349 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/LaunchUIMessages.properties +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/LaunchUIMessages.properties @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2003, 2015 QNX Software Systems and others. +# Copyright (c) 2003, 2016 QNX Software 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 @@ -235,5 +235,7 @@ LocalCDILaunchDelegate.10=Failed to set program arguments, environment or workin ProcessPrompter.Core=core ProcessPrompter.Cores=cores ProcessPrompter.PromptJob=Prompt for Process +ProcessPrompter.ErrProcessConected=Process {0} already connected + ProcessPrompterDialog.New=New... -ProcessPrompterDialog.TitlePrefix=Choose binary for process: \ No newline at end of file +ProcessPrompterDialog.TitlePrefix=Choose binary for process: diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/ProcessPrompter.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/ProcessPrompter.java index 755ef8b2c3b..f95da0bd985 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/ProcessPrompter.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/ProcessPrompter.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008, 2014 QNX Software Systems and others. + * Copyright (c) 2008, 2016 QNX Software 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 @@ -12,6 +12,8 @@ *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.internal.ui.launching; +import java.util.List; + import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin; import org.eclipse.cdt.dsf.gdb.launching.IProcessExtendedInfo; @@ -29,6 +31,9 @@ import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.window.Window; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.dialogs.ISelectionStatusValidator; + +import com.ibm.icu.text.MessageFormat; public class ProcessPrompter implements IStatusHandler { @@ -36,11 +41,13 @@ public class ProcessPrompter implements IStatusHandler { public boolean supportsNewProcess; public boolean remote; public IProcessExtendedInfo[] processList; + public List debuggedProcesses; - public PrompterInfo(boolean supportsNew, boolean remote, IProcessExtendedInfo[] list) { + public PrompterInfo(boolean supportsNew, boolean remote, IProcessExtendedInfo[] list, List debuggedProcs) { supportsNewProcess = supportsNew; this.remote = remote; processList = list; + this.debuggedProcesses = debuggedProcs; } } @@ -60,7 +67,7 @@ public class ProcessPrompter implements IStatusHandler { throw new CoreException(error); } - PrompterInfo prompterInfo = (PrompterInfo) info; + final PrompterInfo prompterInfo = (PrompterInfo) info; IProcessExtendedInfo[] plist = prompterInfo.processList; if (plist == null) { MessageDialog.openError( @@ -178,7 +185,21 @@ public class ProcessPrompter implements IStatusHandler { // Allow for multiple selection dialog.setMultipleSelection(true); - + dialog.setStatusLineAboveButtons(true); + + dialog.setValidator(new ISelectionStatusValidator() { + @Override + public IStatus validate(Object[] selection) { + for (Object sel : selection) { + String pid = Integer.toString(((IProcessExtendedInfo)sel).getPid(), 10); + if (prompterInfo.debuggedProcesses.contains(pid)) { + return new Status(IStatus.ERROR, GdbUIPlugin.getUniqueIdentifier(), + MessageFormat.format(LaunchUIMessages.getString("ProcessPrompter.ErrProcessConected"), pid)); //$NON-NLS-1$ + } + } + return new Status(IStatus.OK, GdbUIPlugin.getUniqueIdentifier(), ""); //$NON-NLS-1$ + } + }); dialog.setElements(plist); if (dialog.open() == Window.OK) { // First check if the user pressed the New button diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/ProcessPrompterDialog.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/ProcessPrompterDialog.java index 111827326a4..545ac26f857 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/ProcessPrompterDialog.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/ProcessPrompterDialog.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2011, 2013 Ericsson and others. + * Copyright (c) 2011, 2016 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 @@ -13,6 +13,8 @@ package org.eclipse.cdt.dsf.gdb.internal.ui.launching; import java.util.Arrays; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.viewers.ILabelProvider; import org.eclipse.swt.SWT; @@ -22,6 +24,8 @@ import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Table; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.dialogs.ISelectionStatusValidator; import org.eclipse.ui.dialogs.TwoPaneElementSelector; /** @@ -130,4 +134,29 @@ public class ProcessPrompterDialog extends TwoPaneElementSelector { public NewExecutableInfo getExecutableInfo() { return fExecInfo; } + + /** + * Validate only upper selected elements. Lower list is always disabled. + * + * @see #createLowerList(Composite) + */ + @Override + protected boolean validateCurrentSelection() { + ISelectionStatusValidator validator = getValidator(); + Object[] elements = getSelectedElements(); + + if (elements.length > 0) { + IStatus status; + if (validator != null) { + status = validator.validate(elements); + } else { + status = new Status(IStatus.OK, PlatformUI.PLUGIN_ID, IStatus.OK, "", //$NON-NLS-1$ + null); + } + updateStatus(status); + return status.isOK(); + } + + return super.validateCurrentSelection(); + } }