diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/launch/ContainerVMNode.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/launch/ContainerVMNode.java index 9ba91cfb4f1..8b4e03427a7 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/launch/ContainerVMNode.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/launch/ContainerVMNode.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006, 2012 Ericsson and others. + * Copyright (c) 2006, 2015 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 @@ -10,6 +10,7 @@ * Wind River Systems - Factored out AbstractContainerVMNode * Patrick Chuong (Texas Instruments) - Add support for icon overlay in the debug view (Bug 334566) * Marc Khouzam (Ericsson) - Respect the "Show Full Path" option for the process name (Bug 378418) + * Marc Khouzam (Ericsson) - Support for exited processes in the debug view (bug 407340) *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.internal.ui.viewmodel.launch; @@ -25,6 +26,7 @@ import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor; import org.eclipse.cdt.dsf.concurrent.RequestMonitor; import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.datamodel.IDMEvent; import org.eclipse.cdt.dsf.debug.service.IProcesses; import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext; import org.eclipse.cdt.dsf.debug.service.IProcesses.IThreadDMData; @@ -41,7 +43,8 @@ import org.eclipse.cdt.dsf.gdb.IGdbDebugPreferenceConstants; import org.eclipse.cdt.dsf.gdb.internal.ui.GdbPinProvider; import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin; import org.eclipse.cdt.dsf.gdb.service.IGDBProcesses.IGdbThreadDMData; -import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.cdt.dsf.gdb.service.IGDBProcesses.IGdbThreadExitedDMData; +import org.eclipse.cdt.dsf.gdb.service.IGDBProcesses.IThreadRemovedDMEvent; import org.eclipse.cdt.dsf.service.DsfSession; import org.eclipse.cdt.dsf.ui.concurrent.ViewerCountingRequestMonitor; import org.eclipse.cdt.dsf.ui.concurrent.ViewerDataRequestMonitor; @@ -58,6 +61,7 @@ import org.eclipse.cdt.dsf.ui.viewmodel.properties.VMDelegatingPropertiesUpdate; import org.eclipse.cdt.ui.CDTSharedImages; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Status; import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementCompareRequest; import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider; @@ -72,7 +76,6 @@ import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.ui.IMemento; -@SuppressWarnings("restriction") public class ContainerVMNode extends AbstractContainerVMNode implements IElementLabelProvider, IElementMementoProvider { @@ -115,6 +118,30 @@ public class ContainerVMNode extends AbstractContainerVMNode provider.setColumnInfo( PropertiesBasedLabelProvider.ID_COLUMN_NO_COLUMNS, new LabelColumnInfo(new LabelAttribute[] { + + /* EXITED CONTAINER LABEL */ + new GdbExecutionContextLabelText( + MessagesForGdbLaunchVM.ContainerVMNode_No_columns__exited_format, + new String[] { + ExecutionContextLabelText.PROP_NAME_KNOWN, + PROP_NAME, + ExecutionContextLabelText.PROP_ID_KNOWN, + ILaunchVMConstants.PROP_ID, + IGdbLaunchVMConstants.PROP_EXIT_CODE_KNOWN, + IGdbLaunchVMConstants.PROP_EXIT_CODE }) { + @Override + public boolean isEnabled(IStatus status, Map properties) { + Boolean exited = (Boolean) properties.get(IGdbLaunchVMConstants.PROP_THREAD_EXITED); + return Boolean.TRUE.equals(exited); + } + }, + /* EXITED CONTAINER IMAGE */ + new LabelImage(DebugUITools.getImageDescriptor(IDebugUIConstants.IMG_OBJS_OS_PROCESS_TERMINATED)) { + { setPropertyNames(new String[] { + IGdbLaunchVMConstants.PROP_THREAD_EXITED }); } + }, + + /* ACTIVE CONTAINER LABEL */ new GdbExecutionContextLabelText( MessagesForGdbLaunchVM.ContainerVMNode_No_columns__text_format, new String[] { @@ -126,6 +153,7 @@ public class ContainerVMNode extends AbstractContainerVMNode IGdbLaunchVMConstants.PROP_CORES_ID, IGdbLaunchVMConstants.PROP_THREAD_SUMMARY_KNOWN, IGdbLaunchVMConstants.PROP_THREAD_SUMMARY }), + new LabelText(MessagesForGdbLaunchVM.ContainerVMNode_No_columns__Error__label, new String[0]), /* RUNNING CONTAINER - RED PIN */ @@ -285,30 +313,31 @@ public class ContainerVMNode extends AbstractContainerVMNode if (update.getProperties().contains(PROP_NAME) || update.getProperties().contains(ILaunchVMConstants.PROP_ID) || - update.getProperties().contains(IGdbLaunchVMConstants.PROP_CORES_ID)) + update.getProperties().contains(IGdbLaunchVMConstants.PROP_CORES_ID) || + update.getProperties().contains(IGdbLaunchVMConstants.PROP_THREAD_EXITED) || + update.getProperties().contains((IGdbLaunchVMConstants.PROP_EXIT_CODE))) { - - IProcesses processService = getServicesTracker().getService(IProcesses.class); - final IProcessDMContext procDmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), IProcessDMContext.class); - - if (processService == null || procDmc == null) { - update.setStatus(DsfUIPlugin.newErrorStatus(IDsfStatusConstants.INVALID_HANDLE, "Service or handle invalid", null)); //$NON-NLS-1$ - } else { - processService.getExecutionData( - procDmc, - new ViewerDataRequestMonitor(getExecutor(), update) { - @Override - public void handleCompleted() { - if (isSuccess()) { - fillThreadDataProperties(update, getData()); - } else { - update.setStatus(getStatus()); - } - countringRm.done(); - } - }); - count++; - } + IProcesses processService = getServicesTracker().getService(IProcesses.class); + final IProcessDMContext procDmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), IProcessDMContext.class); + + if (processService == null || procDmc == null) { + update.setStatus(new Status(IStatus.ERROR, GdbUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_HANDLE, "Service or handle invalid", null)); //$NON-NLS-1$ + } else { + processService.getExecutionData( + procDmc, + new ViewerDataRequestMonitor(getExecutor(), update) { + @Override + public void handleCompleted() { + if (isSuccess()) { + fillThreadDataProperties(update, getData()); + } else { + update.setStatus(getStatus()); + } + countringRm.done(); + } + }); + count++; + } } if (update.getProperties().contains(IGdbLaunchVMConstants.PROP_THREAD_SUMMARY)) { @@ -346,7 +375,16 @@ public class ContainerVMNode extends AbstractContainerVMNode } } } - update.setProperty(IGdbLaunchVMConstants.PROP_CORES_ID, coresStr); + update.setProperty(IGdbLaunchVMConstants.PROP_CORES_ID, coresStr); + + if (data instanceof IGdbThreadExitedDMData) { + update.setProperty(IGdbLaunchVMConstants.PROP_THREAD_EXITED, true); + + Integer exitCode = ((IGdbThreadExitedDMData)data).getExitCode(); + if (exitCode != null) { + update.setProperty(IGdbLaunchVMConstants.PROP_EXIT_CODE, exitCode); + } + } } protected void fillThreadSummary(final IPropertiesUpdate update, final RequestMonitor rm) { @@ -361,7 +399,7 @@ public class ContainerVMNode extends AbstractContainerVMNode final IContainerDMContext procDmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), IContainerDMContext.class); if (processService == null || procDmc == null) { - update.setStatus(DsfUIPlugin.newErrorStatus(IDsfStatusConstants.INVALID_HANDLE, "Service or handle invalid", null)); //$NON-NLS-1$ + update.setStatus(new Status(IStatus.ERROR, GdbUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_HANDLE, "Service or handle invalid", null)); //$NON-NLS-1$ } else { // Fetch all the threads processService.getProcessesBeingDebugged( @@ -373,7 +411,7 @@ public class ContainerVMNode extends AbstractContainerVMNode if (!isSuccess() || !(getData() instanceof IExecutionDMContext[]) || runControl == null) { - update.setStatus(DsfUIPlugin.newErrorStatus(IDsfStatusConstants.INVALID_HANDLE, "Unable to get threads summary", null)); //$NON-NLS-1$ + update.setStatus(new Status(IStatus.ERROR, GdbUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_HANDLE, "Unable to get threads summary", null)); //$NON-NLS-1$ rm.done(); return; } @@ -398,7 +436,14 @@ public class ContainerVMNode extends AbstractContainerVMNode @Override public int getDeltaFlags(Object e) { if (e instanceof ICommandControlShutdownDMEvent) { - return IModelDelta.CONTENT; + return IModelDelta.CONTENT; + } + if (e instanceof IThreadRemovedDMEvent) { + IDMContext dmc = e instanceof IDMEvent ? ((IDMEvent)e).getDMContext() : null; + if (dmc instanceof IProcessDMContext) { + return IModelDelta.CONTENT; + } + return IModelDelta.NO_CHANGE; } return super.getDeltaFlags(e); } @@ -407,6 +452,12 @@ public class ContainerVMNode extends AbstractContainerVMNode public void buildDelta(Object e, final VMDelta parentDelta, final int nodeOffset, final RequestMonitor requestMonitor) { if (e instanceof ICommandControlShutdownDMEvent) { parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT); + } else if (e instanceof IThreadRemovedDMEvent) { + IDMContext dmc = e instanceof IDMEvent ? ((IDMEvent)e).getDMContext() : null; + if (dmc instanceof IProcessDMContext) { + // A process was removed, refresh the parent + parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT); + } } else { super.buildDelta(e, parentDelta, nodeOffset, requestMonitor); return; diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/launch/GdbExecutionContextLabelText.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/launch/GdbExecutionContextLabelText.java index 2874c4b58c5..1f716d8b2de 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/launch/GdbExecutionContextLabelText.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/launch/GdbExecutionContextLabelText.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008, 2012 Wind River Systems and others. + * Copyright (c) 2008, 2015 Wind River Systems and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -7,6 +7,7 @@ * * Contributors: * Wind River Systems - initial API and implementation + * Marc Khouzam (Ericsson) - Support for exited processes in the debug view (bug 407340) *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.internal.ui.viewmodel.launch; @@ -34,7 +35,10 @@ public class GdbExecutionContextLabelText extends ExecutionContextLabelText { } if (IGdbLaunchVMConstants.PROP_THREAD_SUMMARY_KNOWN.equals(propertyName)) { return properties.get(IGdbLaunchVMConstants.PROP_THREAD_SUMMARY) != null ? 1 : 0; - } + } + if (IGdbLaunchVMConstants.PROP_EXIT_CODE_KNOWN.equals(propertyName)) { + return properties.get(IGdbLaunchVMConstants.PROP_EXIT_CODE) != null ? 1 : 0; + } return super.getPropertyValue(propertyName, status, properties); } @@ -43,12 +47,11 @@ public class GdbExecutionContextLabelText extends ExecutionContextLabelText { if (IGdbLaunchVMConstants.PROP_OS_ID_KNOWN.equals(propertyName) || IGdbLaunchVMConstants.PROP_OS_ID.equals(propertyName) || IGdbLaunchVMConstants.PROP_CORES_ID_KNOWN.equals(propertyName) || - IGdbLaunchVMConstants.PROP_CORES_ID.equals(propertyName)) - { - return true; - } - if (IGdbLaunchVMConstants.PROP_THREAD_SUMMARY_KNOWN.equals(propertyName) || - IGdbLaunchVMConstants.PROP_THREAD_SUMMARY.equals(propertyName)) + IGdbLaunchVMConstants.PROP_CORES_ID.equals(propertyName) || + IGdbLaunchVMConstants.PROP_THREAD_SUMMARY_KNOWN.equals(propertyName) || + IGdbLaunchVMConstants.PROP_THREAD_SUMMARY.equals(propertyName) || + IGdbLaunchVMConstants.PROP_EXIT_CODE_KNOWN.equals(propertyName) || + IGdbLaunchVMConstants.PROP_EXIT_CODE.equals(propertyName)) { return true; } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/launch/IGdbLaunchVMConstants.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/launch/IGdbLaunchVMConstants.java index 9609bde2c15..6a93f2a0f1b 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/launch/IGdbLaunchVMConstants.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/launch/IGdbLaunchVMConstants.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008, 2010 Wind River Systems and others. + * Copyright (c) 2008, 2015 Wind River Systems and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -8,6 +8,7 @@ * Contributors: * Wind River Systems - initial API and implementation * Patrick Chuong (Texas Instruments) - Add support for icon overlay in the debug view (Bug 334566) + * Marc Khouzam (Ericsson) - Support for exited processes in the debug view (bug 407340) *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.internal.ui.viewmodel.launch; @@ -42,4 +43,18 @@ public interface IGdbLaunchVMConstants { public static final String PROP_THREAD_SUMMARY_KNOWN = "thread_summary_known"; //$NON-NLS-1$ public static final String PROP_THREAD_SUMMARY = "thread_summary"; //$NON-NLS-1$ + + /** + * If this property is set, it indicates the process or thread should be shown as exited. + */ + public static final String PROP_THREAD_EXITED = "thread_exited"; //$NON-NLS-1$ + + /** + * Value 0 means it's not known. Value 1, means it's known. + */ + public static final String PROP_EXIT_CODE_KNOWN = "exit_code_known"; //$NON-NLS-1$ + /** + * If set, the value of the property indicates the exit code returned. + */ + public static final String PROP_EXIT_CODE = "exit_code"; //$NON-NLS-1$ } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/launch/MessagesForGdbLaunchVM.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/launch/MessagesForGdbLaunchVM.java index 16bda0a0a49..f17ac34b22a 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/launch/MessagesForGdbLaunchVM.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/launch/MessagesForGdbLaunchVM.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008, 2012 Wind River Systems and others. + * Copyright (c) 2008, 2015 Wind River Systems and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -7,6 +7,7 @@ * * Contributors: * Wind River Systems - initial API and implementation + * Marc Khouzam (Ericsson) - Support for exited processes in the debug view (bug 407340) *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.internal.ui.viewmodel.launch; @@ -20,6 +21,7 @@ public class MessagesForGdbLaunchVM extends NLS { public static String ThreadVMNode_No_columns__text_format; public static String ThreadVMNode_No_columns__Error__label; public static String ContainerVMNode_No_columns__text_format; + public static String ContainerVMNode_No_columns__exited_format; public static String ContainerVMNode_No_columns__Error__label; /** since 2.3 */ public static String ContainerVMNode_filtered_running_threads; diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/launch/MessagesForGdbLaunchVM.properties b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/launch/MessagesForGdbLaunchVM.properties index 8e6e3a16fff..20ae9f6616d 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/launch/MessagesForGdbLaunchVM.properties +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/launch/MessagesForGdbLaunchVM.properties @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2007, 2014 Wind River Systems and others. +# Copyright (c) 2007, 2015 Wind River Systems and others. # All rights reserved. This program and the accompanying materials # are made available under the terms of the Eclipse Public License v1.0 # which accompanies this distribution, and is available at @@ -9,6 +9,7 @@ # Wind River Systems - initial API and implementation # Marc Khouzam (Ericsson) - Update format of thread string to better display # Thread name (Bug 378154) +# Marc Khouzam (Ericsson) - Support for exited processes in the debug view (bug 407340) ############################################################################### # {0} - name available, 0=not available/1=available @@ -38,6 +39,14 @@ ThreadVMNode_No_columns__Error__label= # {7} - Thread summary in text format ContainerVMNode_No_columns__text_format={0,choice,0#Process|1#{1}}{2,choice,0#|1# [{3}]}{4,choice,0#|1# [cores: {5}]}{6,choice,0#|1# {7}} +# {0} - name available, 0=not available/1=available +# {1} - name +# {2} - ID available, 0=not available/1=available +# {3} - ID +# {4} - Exit code available, 0=not available/1=available +# {5} - Exit code +ContainerVMNode_No_columns__exited_format={0,choice,0#Process|1#{1}}{2,choice,0#|1# [{3}]} + ContainerVMNode_No_columns__Error__label= -ContainerVMNode_filtered_running_threads=filtered running threads \ No newline at end of file +ContainerVMNode_filtered_running_threads=filtered running threads diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses_7_0.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses_7_0.java index 89f7eac35e6..78ed645e640 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses_7_0.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses_7_0.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008, 2014 Ericsson and others. + * Copyright (c) 2008, 2015 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,16 +13,22 @@ * Andy Jin (QNX) - Not output thread osId as a string when it is null (Bug 397039) * Marc Khouzam (Ericsson) - Move IBreakpointsTargetDMContext from MIContainerDMC * to GDBContainerDMC to ease inheritance (Bug 389945) + * Marc Khouzam (Ericsson) - Support for exited processes in the debug view (bug 407340) *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.service; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.IProcessInfo; @@ -101,6 +107,12 @@ import org.osgi.framework.BundleContext; public class GDBProcesses_7_0 extends AbstractDsfService implements IGDBProcesses, ICachingService, IEventListener { + /** + * The maximum amount of exited processes we can show. + * Each one is shown in the debug view. + */ + private final static int MAX_NUMBER_EXITED_PROCESS = 5; + // Below is the context hierarchy that is implemented between the // MIProcesses service and the MIRunControl service for the MI // implementation of DSF: @@ -345,7 +357,55 @@ public class GDBProcesses_7_0 extends AbstractDsfService return baseHashCode(); } } - + + /** + * A process context representing a process that has exited. + * Since an exited process no longer has a pid, we need another way + * of characterizing it. We use the groupId instead. + * Note that with GDB 7.0 and 7.1, the groupId is the pid, so that + * does not help us, but since we only handle single-process debugging + * for those versions of GDB, we don't need any id to know we are + * dealing with our single process. + * Starting with GDB 7.2, we handle multi-process, but then we + * can use the groupId as a persistent identifier of each process, + * even an exited one. + * @since 4.7 + */ + @Immutable + protected static class MIExitedProcessDMC extends MIProcessDMC + { + private final String fGroupId; + + public MIExitedProcessDMC(String sessionId, ICommandControlDMContext controlDmc, String pid, String groupId) { + super(sessionId, controlDmc, pid); + fGroupId = groupId; + } + + public String getGroupId() { return fGroupId; } + + @Override + public String toString() { return super.toString() + ".group[" + getGroupId() + "]"; } //$NON-NLS-1$ //$NON-NLS-2$ + + @Override + public boolean equals(Object obj) { + if (!super.equals(obj)) { + return false; + } + + MIExitedProcessDMC other = (MIExitedProcessDMC)obj; + if (fGroupId == null || other.fGroupId == null) { + return fGroupId == null && other.fGroupId == null; + } + + return fGroupId.equals(other.fGroupId); + } + + @Override + public int hashCode() { + return super.hashCode() ^ (fGroupId == null ? 0 : fGroupId.hashCode()); + } + } + /** * The data of a corresponding thread or process. */ @@ -371,6 +431,39 @@ public class GDBProcesses_7_0 extends AbstractDsfService } } + /** + * The data corresponding to an exited process. + * @since 4.7 + */ + @Immutable + protected static class MIExitedProcessDMData implements IGdbThreadExitedDMData { + final String fName; + final String fId; + final Integer fExitCode; + + public MIExitedProcessDMData(String name, String id, Integer exitCode) { + fName = name; + fId = id; + fExitCode = exitCode; + } + + @Override + public String getId() { return fId; } + + @Override + public String getName() { return fName; } + + @Override + public boolean isDebuggerAttached() { + return false; + } + + @Override + public Integer getExitCode() { + return fExitCode; + } + } + /** * This class provides an implementation of both a process context and process data. * It is used to be able to return a list of processes including their data all at once. @@ -449,6 +542,17 @@ public class GDBProcesses_7_0 extends AbstractDsfService } } + /** + * @since 4.7 + */ + protected static class ProcessRemovedDMEvent extends AbstractDMEvent + implements IThreadRemovedDMEvent + { + public ProcessRemovedDMEvent(IProcessDMContext context) { + super(context); + } + } + /** * A map of thread id to thread group id. We use this to find out to which threadGroup a thread belongs. */ @@ -483,8 +587,73 @@ public class GDBProcesses_7_0 extends AbstractDsfService // This map also serves as a list of processes we are currently debugging. // This is important because we cannot always ask GDB for the list, since it may // be running at the time. Bug 303503 - private Map fDebuggedProcessesAndNames = new HashMap(); - + private Map fDebuggedProcessesAndNames = new HashMap<>(); + + /** + * Information about an exited process + * @since 4.7 + */ + protected class ExitedProcInfo { + private String pid; + private String name; + private Integer exitCode; + + public ExitedProcInfo(String aPid, String aName) { + pid = aPid; + name = aName; + } + + protected String getPid() { + return pid; + } + + protected String getName() { + return name; + } + + protected Integer getExitCode() { + return exitCode; + } + + protected void setExitCode(Integer code) { + exitCode = code; + } + } + + /** + * A LRU (least-recently-used) map that limits the amount of exited process list. + * Once the limit is reached, oldest exited processes are automatically removed + * when new ones are inserted. This avoids the risk of growing the list + * of exited processes too much and showing too many in the debug view. + */ + private class LRUExitedProcessMap extends LinkedHashMap { + public static final long serialVersionUID = 0; + + @Override + protected boolean removeEldestEntry(Entry eldest) { + return size() > MAX_NUMBER_EXITED_PROCESS; + } + } + + /** + * Map of groupId to ExitedProcInfo. + * This map contains the information of each process that has exited. + * Note that we must maintain this information ourselves since GDB + * sometimes prunes its list of inferiors, which implies we cannot + * count on GDB to keep track of exited processes. + */ + private Map fProcExitedMap = new LRUExitedProcessMap(); + + /** + * Set of groupId of processes that we detached from. + * The content is very short-lived as it is only kept until + * we receive the =thread-group-exited event from GDB + * and need to know if the process in question was detached from. + * Using this set, we can know if we should store the process + * in the fExitedProcesses map or not. + */ + private Set fProcDetachedSet = new HashSet<>(); + private static final String FAKE_THREAD_ID = "0"; //$NON-NLS-1$ /** @@ -625,7 +794,17 @@ public class GDBProcesses_7_0 extends AbstractDsfService protected void setIsInitialProcess(boolean isInitial) { fInitialProcess = isInitial; } + + /**@since 4.7 */ + protected Map getExitedProcesses() { + return fProcExitedMap; + } + /** @since 4.7 */ + protected Set getDetachedProcesses() { + return fProcDetachedSet; + } + /** * Returns the groupId that is associated with the provided pId * @since 4.0 @@ -650,7 +829,24 @@ public class GDBProcesses_7_0 extends AbstractDsfService public IProcessDMContext createProcessContext(ICommandControlDMContext controlDmc, String pid) { return new MIProcessDMC(getSession().getId(), controlDmc, pid); } - + + /** + * Create a special context describing a process that has exited. + * @param controlDmc Its parent context. + * @param groupId The GDB groupId to which this process refers to. Since an exited process no longer + * has a pid, we use this id to characterize it uniquely. + * Note that with GDB 7.0 and 7.1, the groupId is the pid, so that + * does not help us, but since we only handle single-process debugging + * for those versions of GDB, we don't need any id to know we are + * dealing with our single process. + * Starting with GDB 7.2, we handle multi-process, but then we + * can use the groupId as a persistent identifier of each process, + * even an exited one. + */ + private IProcessDMContext createExitedProcessContext(ICommandControlDMContext controlDmc, String pid, String groupId) { + return new MIExitedProcessDMC(getSession().getId(), controlDmc, pid, groupId); + } + @Override public IMIExecutionDMContext createExecutionContext(IContainerDMContext containerDmc, IThreadDMContext threadDmc, @@ -672,7 +868,6 @@ public class GDBProcesses_7_0 extends AbstractDsfService // In such a case, we choose the first process we find // This works when we run a single process // but will break for multi-process!!! - // khouzam if (getThreadToGroupMap().isEmpty()) { groupId = MIProcesses.UNIQUE_GROUP_ID; } else { @@ -713,6 +908,11 @@ public class GDBProcesses_7_0 extends AbstractDsfService @Override public IMIExecutionDMContext[] getExecutionContexts(IMIContainerDMContext containerDmc) { + if (isExitedProcess(containerDmc)) { + // No threads for an exited process + return new IMIExecutionDMContext[0]; + } + String groupId = containerDmc.getGroupId(); List execDmcList = new ArrayList(); Iterator> iterator = getThreadToGroupMap().entrySet().iterator(); @@ -732,6 +932,20 @@ public class GDBProcesses_7_0 extends AbstractDsfService @Override public void getExecutionData(IThreadDMContext dmc, final DataRequestMonitor rm) { + if (dmc instanceof MIExitedProcessDMC) { + ExitedProcInfo info = getExitedProcesses().get(((MIExitedProcessDMC)dmc).getGroupId()); + if (info != null) { + rm.done(new MIExitedProcessDMData(info.getName(), info.getPid(), info.getExitCode())); + } else { + // This can happen for example, when restarting an exited process, + // where we've deleted the process from our table, but it has + // yet to be cleaned up from the view + rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Unavailable info about exited process", null)); //$NON-NLS-1$ + } + + return; + } + if (dmc instanceof IMIProcessDMContext) { String id = ((IMIProcessDMContext)dmc).getProcId(); String name = null; @@ -811,7 +1025,11 @@ public class GDBProcesses_7_0 extends AbstractDsfService @Override public void getDebuggingContext(IThreadDMContext dmc, DataRequestMonitor rm) { - if (dmc instanceof MIProcessDMC) { + if (dmc instanceof MIExitedProcessDMC) { + MIExitedProcessDMC exitedProc = (MIExitedProcessDMC)dmc; + IMIContainerDMContext containerDmc = createContainerContext(exitedProc, exitedProc.getGroupId()); + rm.setData(containerDmc); + } else if (dmc instanceof MIProcessDMC) { MIProcessDMC procDmc = (MIProcessDMC)dmc; IMIContainerDMContext containerDmc = createContainerContext(procDmc, getGroupFromPid(procDmc.getProcId())); rm.setData(containerDmc); @@ -970,17 +1188,33 @@ public class GDBProcesses_7_0 extends AbstractDsfService return fNumConnected > 0; } + private boolean isExitedProcess(IDMContext dmc) { + return DMContexts.getAncestorOfType(dmc, MIExitedProcessDMC.class) != null; + } + @Override public void canDetachDebuggerFromProcess(IDMContext dmc, DataRequestMonitor rm) { - rm.setData(doCanDetachDebuggerFromProcess()); - rm.done(); + MIExitedProcessDMC exitedProc = DMContexts.getAncestorOfType(dmc, MIExitedProcessDMC.class); + if (exitedProc != null) { + // Allow to use the disconnect button to remove an exited process + rm.done(true); + return; + } + rm.done(doCanDetachDebuggerFromProcess()); } @Override public void detachDebuggerFromProcess(final IDMContext dmc, final RequestMonitor rm) { - + MIExitedProcessDMC exitedProc = DMContexts.getAncestorOfType(dmc, MIExitedProcessDMC.class); + if (exitedProc != null) { + // For an exited process, simply remove the entry from our table to stop showing it. + getExitedProcesses().remove(exitedProc.getGroupId()); + getSession().dispatchEvent(new ProcessRemovedDMEvent(exitedProc), null); + return; + } + ICommandControlDMContext controlDmc = DMContexts.getAncestorOfType(dmc, ICommandControlDMContext.class); - IMIProcessDMContext procDmc = DMContexts.getAncestorOfType(dmc, IMIProcessDMContext.class); + final IMIProcessDMContext procDmc = DMContexts.getAncestorOfType(dmc, IMIProcessDMContext.class); if (controlDmc != null && procDmc != null) { if (!doCanDetachDebuggerFromProcess()) { @@ -994,9 +1228,24 @@ public class GDBProcesses_7_0 extends AbstractDsfService fBackend.interrupt(); } + // Remember that this process was detached so we don't show it as an exited process. + // We must set this before sending the detach command to gdb to avoid race conditions + final IMIContainerDMContext containerDmc = DMContexts.getAncestorOfType(dmc, IMIContainerDMContext.class); + if (containerDmc != null) { + getDetachedProcesses().add(containerDmc.getGroupId()); + } fCommandControl.queueCommand( fCommandFactory.createMITargetDetach(controlDmc, procDmc.getProcId()), - new DataRequestMonitor(getExecutor(), rm)); + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleFailure() { + // The detach failed + if (containerDmc != null) { + getDetachedProcesses().remove(containerDmc.getGroupId()); + } + super.handleFailure(); + } + }); } else { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid context.", null)); //$NON-NLS-1$ rm.done(); @@ -1005,7 +1254,12 @@ public class GDBProcesses_7_0 extends AbstractDsfService @Override public void canTerminate(IThreadDMContext thread, DataRequestMonitor rm) { - rm.setData(true); + if (thread instanceof MIExitedProcessDMC) { + // Allow pressing the terminate button to remove an exited process + rm.setData(true); + } else { + rm.setData(true); + } rm.done(); } @@ -1057,6 +1311,12 @@ public class GDBProcesses_7_0 extends AbstractDsfService final ICommandControlDMContext controlDmc = DMContexts.getAncestorOfType(dmc, ICommandControlDMContext.class); final IMIContainerDMContext containerDmc = DMContexts.getAncestorOfType(dmc, IMIContainerDMContext.class); if (containerDmc != null) { + if (isExitedProcess(containerDmc)) { + // No threads for an exited process + rm.done(new IMIExecutionDMContext[0]); + return; + } + fThreadCommandCache.execute( fCommandFactory.createMIListThreadGroups(controlDmc, containerDmc.getGroupId()), new DataRequestMonitor(getExecutor(), rm) { @@ -1067,13 +1327,34 @@ public class GDBProcesses_7_0 extends AbstractDsfService } }); } else { + + final DataRequestMonitor addExitedDRM = + new ImmediateDataRequestMonitor(rm) { + @Override + protected void handleCompleted() { + List containerDmcs = new ArrayList<>(Arrays.asList(getData())); + + // Add the exited processes to our list in reverse order of insertion so that + // the latest exited process is at the top + List> entries = new ArrayList<>(getExitedProcesses().entrySet()); + for (int i = entries.size() - 1; i >= 0 ; i--) { + Entry entry = entries.get(i); + String groupId = entry.getKey(); + String pid = entry.getValue().getPid(); + IProcessDMContext processDmc = createExitedProcessContext(controlDmc, pid, groupId); + containerDmcs.add(createContainerContext(processDmc, groupId)); + } + + rm.done(containerDmcs.toArray(new IMIContainerDMContext[containerDmcs.size()])); + }; + }; + fContainerCommandCache.execute( fCommandFactory.createMIListThreadGroups(controlDmc), - new DataRequestMonitor(getExecutor(), rm) { + new DataRequestMonitor(getExecutor(), addExitedDRM) { @Override protected void handleSuccess() { - rm.setData(makeContainerDMCs(controlDmc, getData().getGroupList())); - rm.done(); + addExitedDRM.done(makeContainerDMCs(controlDmc, getData().getGroupList())); } @Override protected void handleFailure() { @@ -1083,8 +1364,7 @@ public class GDBProcesses_7_0 extends AbstractDsfService for (String groupId : getGroupToPidMap().keySet()) { containerDmcs[i++] = createContainerContextFromGroupId(controlDmc, groupId); } - rm.setData(containerDmcs); - rm.done(); + addExitedDRM.done(containerDmcs); } }); } @@ -1244,9 +1524,13 @@ public class GDBProcesses_7_0 extends AbstractDsfService @Override public void terminate(IThreadDMContext thread, final RequestMonitor rm) { - // For a core session, there is no concept of killing the inferior, - // so lets kill GDB - if (fBackend.getSessionType() == SessionType.CORE) { + if (thread instanceof MIExitedProcessDMC) { + // For an exited process, simply remove the entry from our table to stop showing it. + getExitedProcesses().remove(((MIExitedProcessDMC)thread).getGroupId()); + getSession().dispatchEvent(new ProcessRemovedDMEvent((IProcessDMContext)thread), null); + } else if (fBackend.getSessionType() == SessionType.CORE) { + // For a core session, there is no concept of killing the inferior, + // so lets kill GDB fCommandControl.terminate(rm); } else if (thread instanceof IMIProcessDMContext) { getDebuggingContext( @@ -1350,6 +1634,10 @@ public class GDBProcesses_7_0 extends AbstractDsfService if (!isSuccess()) { fProcRestarting = false; } + + // In case the process we restarted was already exited, remove it from our list + getExitedProcesses().remove(groupId); + setData(getData()); super.handleCompleted(); }; @@ -1449,6 +1737,7 @@ public class GDBProcesses_7_0 extends AbstractDsfService if (e instanceof ContainerStartedDMEvent) { fContainerCommandCache.reset(); fNumConnected++; + fProcRestarting = false; } else { fThreadCommandCache.reset(); } @@ -1475,7 +1764,6 @@ public class GDBProcesses_7_0 extends AbstractDsfService fCommandControl.terminate(new ImmediateRequestMonitor()); } } - fProcRestarting = false; } else { fThreadCommandCache.reset(); } @@ -1617,8 +1905,13 @@ public class GDBProcesses_7_0 extends AbstractDsfService if (groupId != null) { String pId = getGroupToPidMap().remove(groupId); - // GDB is no longer debugging this process. Remove it from our list. - fDebuggedProcessesAndNames.remove(pId); + // GDB is no longer debugging this process. Remove it from our list + String name = fDebuggedProcessesAndNames.remove(pId); + if (!fProcRestarting && !getDetachedProcesses().remove(groupId)) { + // If the process is not restarting and was not detached, + // store it in the list of exited processes. + getExitedProcesses().put(groupId, new ExitedProcInfo(pId, name)); + } // Remove any entries for that group from our thread to group map // When detaching from a group, we won't have received any thread-exited event diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses_7_1.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses_7_1.java index 886f960ebd0..09821e5fa93 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses_7_1.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses_7_1.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2010, 2014 Ericsson and others. + * Copyright (c) 2010, 2015 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 @@ -10,6 +10,7 @@ * Andy Jin (QNX) - Not output thread osId as a string when it is null (Bug 397039) * Alvaro Sanchez-Leon - Bug 451396 - Improve extensibility to process MI "-thread-info" results * Simon Marchi (Ericsson) - Bug 378154 - Pass thread name from MIThread to the data model + * Marc Khouzam (Ericsson) - Support for exited processes in the debug view (bug 407340) *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.service; @@ -135,6 +136,12 @@ public class GDBProcesses_7_1 extends GDBProcesses_7_0 { @Override protected void handleSuccess() { final IThreadDMData firstLevelData = getData(); + + // No need to go further if we are dealing with an exited process + if (firstLevelData instanceof IGdbThreadExitedDMData) { + rm.done(firstLevelData); + return; + } ICommandControlDMContext controlDmc = DMContexts.getAncestorOfType(dmc, ICommandControlDMContext.class); final String groupId = getGroupFromPid(((IMIProcessDMContext)dmc).getProcId()); @@ -217,7 +224,7 @@ public class GDBProcesses_7_1 extends GDBProcesses_7_0 { String name = thread.getName(); String core = thread.getCore(); - return new MIThreadDMData_7_1(name == null ? "" : name, id, core == null ? null : new String[] { core }); + return new MIThreadDMData_7_1(name == null ? "" : name, id, core == null ? null : new String[] { core }); //$NON-NLS-1$ } @DsfServiceEventHandler 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 b9ebfb20a4d..7f85d67a11b 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 @@ -495,6 +495,12 @@ public class GDBProcesses_7_2 extends GDBProcesses_7_1 implements IMultiTerminat @Override public void detachDebuggerFromProcess(IDMContext dmc, final RequestMonitor rm) { + MIExitedProcessDMC exitedProc = DMContexts.getAncestorOfType(dmc, MIExitedProcessDMC.class); + if (exitedProc != null) { + super.detachDebuggerFromProcess(dmc, rm); + return; + } + ICommandControlDMContext controlDmc = DMContexts.getAncestorOfType(dmc, ICommandControlDMContext.class); final IMIContainerDMContext containerDmc = DMContexts.getAncestorOfType(dmc, IMIContainerDMContext.class); @@ -509,7 +515,10 @@ public class GDBProcesses_7_2 extends GDBProcesses_7_1 implements IMultiTerminat if (runControl != null && !runControl.isTargetAcceptingCommands()) { fBackend.interrupt(); } - + + // Remember that this process was detached so we don't show it as an exited process. + // We must set this before sending the detach command to gdb to avoid race conditions + getDetachedProcesses().add(containerDmc.getGroupId()); fCommandControl.queueCommand( fCommandFactory.createMITargetDetach(controlDmc, containerDmc.getGroupId()), new DataRequestMonitor(getExecutor(), rm) { @@ -529,7 +538,14 @@ public class GDBProcesses_7_2 extends GDBProcesses_7_1 implements IMultiTerminat // Also, with GDB 7.2, removing the inferior does not work because of another bug, so we just don't do it. fCommandControl.queueCommand( fCommandFactory.createMITargetDetach(containerDmc), - new DataRequestMonitor(getExecutor(), rm)); + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleFailure() { + // Detach failed + getDetachedProcesses().remove(containerDmc.getGroupId()); + super.handleFailure(); + }; + }); } } }); diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses_7_3.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses_7_3.java index 76fd7240c73..dbf8fe657d9 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses_7_3.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses_7_3.java @@ -16,6 +16,8 @@ import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; import org.eclipse.cdt.dsf.concurrent.DsfExecutor; import org.eclipse.cdt.dsf.concurrent.Sequence; import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.cdt.dsf.mi.service.command.events.MIThreadGroupExitedEvent; +import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; import org.eclipse.cdt.dsf.service.DsfSession; /** @@ -35,5 +37,25 @@ public class GDBProcesses_7_3 extends GDBProcesses_7_2_1 { DataRequestMonitor rm) { return new StartOrRestartProcessSequence_7_3(executor, containerDmc, attributes, restart, rm); } + + @Override + @DsfServiceEventHandler + public void eventDispatched(MIThreadGroupExitedEvent e) { + super.eventDispatched(e); + + // Cache the exit code if there is one + String groupId = e.getGroupId(); + String exitCode = e.getExitCode(); + if (groupId != null && exitCode != null) { + ExitedProcInfo info = getExitedProcesses().get(groupId); + if (info != null) { + try { + // Must use 'decode' since GDB returns an octal value + info.setExitCode(Integer.decode(exitCode)); + } catch (NumberFormatException exception) { + } + } + } + } } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/IGDBProcesses.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/IGDBProcesses.java index 9191a28b573..17d7eb933f0 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/IGDBProcesses.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/IGDBProcesses.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008, 2011 Ericsson and others. + * Copyright (c) 2008, 2015 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 @@ -8,6 +8,7 @@ * Contributors: * Ericsson - initial API and implementation * Ericsson - added support for core-awareness + * Marc Khouzam (Ericsson) - Support for exited processes in the debug view (bug 407340) *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.service; @@ -15,6 +16,7 @@ import java.util.Map; import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.datamodel.IDMEvent; import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; import org.eclipse.cdt.dsf.mi.service.IMIContainerDMContext; import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext; @@ -29,7 +31,7 @@ public interface IGDBProcesses extends IMIProcesses { * * @since 4.0 */ - public interface IGdbThreadDMData extends IThreadDMData { + interface IGdbThreadDMData extends IThreadDMData { /** * @return The list of identifiers of the cores on which the thread * or process is currently located. A thread will typically @@ -47,6 +49,27 @@ public interface IGDBProcesses extends IMIProcesses { String getOwner(); } + /** + * This interface describes an exited thread/process. + * + * @since 4.7 + */ + interface IGdbThreadExitedDMData extends IThreadDMData { + /** + * @return The exit code of this process. + * Returns null if the exit code is not known. + */ + Integer getExitCode(); + } + + /** + * Indicates that a process or thread is no longer being tracked by + * the session. This event usually refers to exited elements that + * were still being shown to the user but that have now been removed. + * @since 4.7 + */ + interface IThreadRemovedDMEvent extends IDMEvent {} + /** * Get a list of all execution contexts belonging to a container. This call is synchronous, * unlike the call to getProcessesBeingDebugged(). However, some services may not be able diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIListThreadGroupsInfo.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIListThreadGroupsInfo.java index 7a7b607624c..a71ba6db151 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIListThreadGroupsInfo.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIListThreadGroupsInfo.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008, 2014 Ericsson and others. + * Copyright (c) 2008, 2015 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 @@ -9,6 +9,7 @@ * Ericsson - Initial API and implementation * Xavier Raynaud (Kalray) - MIThread can be overridden (Bug 429124) * Alvaro Sanchez-Leon - Bug 451396 - Improve extensibility to process MI "-thread-info" results + * Marc Khouzam (Ericsson) - Support new "exit-code" MI parameter (Bug 407340) *******************************************************************************/ package org.eclipse.cdt.dsf.mi.service.command.output; @@ -164,6 +165,14 @@ import org.eclipse.cdt.dsf.concurrent.Immutable; * -list-thread-groups --available * ^done,groups=[{id="19445",type="process",description="gdb.7.2 -i mi testing/a.out",user="lmckhou"},{id="19451",type="process",description="/local/lmckhou/testing/a.out",user="lmckhou"},{id="19462",type="process",description="sleep 5",user="lmckhou"}] * + * GDB 7.9 + * + * -list-thread-groups + * ^done,groups=[{id="i1",type="process",exit-code="0",executable="/home/lmckhou/runtime-TestDSF/DSFTestApp/Debug/DSFTestApp"}] + * + * -list-thread-groups + * ^done,groups=[{id="i1",type="process",exit-code="03",executable="/home/lmckhou/runtime-TestDSF/DSFTestApp/Debug/DSFTestApp"}] + * * @since 1.1 */ @@ -186,6 +195,13 @@ public class MIListThreadGroupsInfo extends MIInfo { String[] getCores(); /**@since 4.0 */ String getExecutable(); + + /** + * @return the exit code of this thread group. + * null if not applicable or not available. + * @since 4.7 + */ + Integer getExitCode(); } /** @@ -207,9 +223,16 @@ public class MIListThreadGroupsInfo extends MIInfo { final String[] fCores; final String fExecutable; final MIThread[] fThreadList; - + final Integer fExitCode; + public ThreadGroupInfo(String id, String description, String type, String pid, - String user, String[] cores, String exec, MIThread[] threads) { + String user, String[] cores, String exec, MIThread[] threads) { + this(id, description, type, pid, user, cores, exec, threads, null); + } + + /** @since 4.7 */ + public ThreadGroupInfo(String id, String description, String type, String pid, + String user, String[] cores, String exec, MIThread[] threads, Integer exitCode) { fGroupId = id; fDescription = description; fType = type; @@ -222,6 +245,8 @@ public class MIListThreadGroupsInfo extends MIInfo { fName = parseName(fDescription); fThreadList = threads; + + fExitCode = exitCode; } protected String parseName(String desc) { @@ -287,6 +312,12 @@ public class MIListThreadGroupsInfo extends MIInfo { @Override public MIThread[] getThreads() { return fThreadList; } + + /** @since 4.7 */ + @Override + public Integer getExitCode() { + return fExitCode; + } } @@ -346,6 +377,7 @@ public class MIListThreadGroupsInfo extends MIInfo { String id, desc, type, pid, exec, user; id = desc = type = pid = exec = user = "";//$NON-NLS-1$ MIThread[] threads = null; + Integer exitCode = null; String[] cores = null; @@ -382,24 +414,35 @@ public class MIListThreadGroupsInfo extends MIInfo { user = str.trim(); } } else if (var.equals("cores")) { //$NON-NLS-1$ - // Staring with GDB 7.1 + // Starting with GDB 7.1 MIValue value = result.getMIValue(); if (value instanceof MIList) { cores = parseCores((MIList)value); } } else if (var.equals("executable")) { //$NON-NLS-1$ - // Staring with GDB 7.2 + // Starting with GDB 7.2 MIValue value = result.getMIValue(); if (value instanceof MIConst) { String str = ((MIConst)value).getCString(); exec = str.trim(); } } else if (var.equals("threads")) { //$NON-NLS-1$ - // Staring with GDB 7.1 + // Starting with GDB 7.1 MIValue value = result.getMIValue(); if (value instanceof MIList) { threads = parseThreads(((MIList)value)); } + } else if (var.equals("exit-code")) { //$NON-NLS-1$ + // Starting with GDB 7.9 + MIValue value = result.getMIValue(); + if (value instanceof MIConst) { + String str = ((MIConst)value).getCString(); + try { + // Must use 'decode' as GDB returns the value in octal format + exitCode = Integer.decode(str.trim()); + } catch (NumberFormatException e) { + } + } } } // In the case of -list-thread-groups --available, the pid field is not present, but the @@ -410,7 +453,7 @@ public class MIListThreadGroupsInfo extends MIInfo { if (pid.equals("") && !desc.equals("")) { //$NON-NLS-1$ //$NON-NLS-2$ pid = id; } - fGroupList[i] = new ThreadGroupInfo(id, desc, type, pid, user, cores, exec, threads); + fGroupList[i] = new ThreadGroupInfo(id, desc, type, pid, user, cores, exec, threads, exitCode); } }