mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-07-21 16:05:25 +02:00
Bug 407340 - Keep showing in the Debug view a process that has exited
Change-Id: Ib4a1c80d99b1a311c781ea1f481943982cdf0cbd Signed-off-by: Marc Khouzam <marc.khouzam@ericsson.com>
This commit is contained in:
parent
a19bbeb962
commit
d533adec3c
11 changed files with 563 additions and 79 deletions
|
@ -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<String, Object> 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,14 +313,15 @@ 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$
|
||||
update.setStatus(new Status(IStatus.ERROR, GdbUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_HANDLE, "Service or handle invalid", null)); //$NON-NLS-1$
|
||||
} else {
|
||||
processService.getExecutionData(
|
||||
procDmc,
|
||||
|
@ -347,6 +376,15 @@ public class ContainerVMNode extends AbstractContainerVMNode
|
|||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
@ -400,6 +438,13 @@ public class ContainerVMNode extends AbstractContainerVMNode
|
|||
if (e instanceof ICommandControlShutdownDMEvent) {
|
||||
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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
@ -35,6 +36,9 @@ 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;
|
||||
}
|
||||
|
|
|
@ -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 <code>0</code> means it's not known. Value <code>1</code>, 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$
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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=<unavailable>
|
|||
# {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=<terminated{4,choice,0#|1#, exit value: {5}}>{0,choice,0#Process|1#{1}}{2,choice,0#|1# [{3}]}
|
||||
|
||||
ContainerVMNode_No_columns__Error__label=<unavailable>
|
||||
|
||||
ContainerVMNode_filtered_running_threads=filtered running threads
|
|
@ -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:
|
||||
|
@ -346,6 +358,54 @@ public class GDBProcesses_7_0 extends AbstractDsfService
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<IThreadDMContext>
|
||||
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,7 +587,72 @@ 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<String, String> fDebuggedProcessesAndNames = new HashMap<String, String>();
|
||||
private Map<String, String> 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<String, ExitedProcInfo> {
|
||||
public static final long serialVersionUID = 0;
|
||||
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Entry<String, ExitedProcInfo> 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<String, ExitedProcInfo> 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<String> fProcDetachedSet = new HashSet<>();
|
||||
|
||||
private static final String FAKE_THREAD_ID = "0"; //$NON-NLS-1$
|
||||
|
||||
|
@ -626,6 +795,16 @@ public class GDBProcesses_7_0 extends AbstractDsfService
|
|||
fInitialProcess = isInitial;
|
||||
}
|
||||
|
||||
/**@since 4.7 */
|
||||
protected Map<String, ExitedProcInfo> getExitedProcesses() {
|
||||
return fProcExitedMap;
|
||||
}
|
||||
|
||||
/** @since 4.7 */
|
||||
protected Set<String> getDetachedProcesses() {
|
||||
return fProcDetachedSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the groupId that is associated with the provided pId
|
||||
* @since 4.0
|
||||
|
@ -651,6 +830,23 @@ public class GDBProcesses_7_0 extends AbstractDsfService
|
|||
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<IMIExecutionDMContext> execDmcList = new ArrayList<IMIExecutionDMContext>();
|
||||
Iterator<Map.Entry<String, String>> iterator = getThreadToGroupMap().entrySet().iterator();
|
||||
|
@ -732,6 +932,20 @@ public class GDBProcesses_7_0 extends AbstractDsfService
|
|||
|
||||
@Override
|
||||
public void getExecutionData(IThreadDMContext dmc, final DataRequestMonitor<IThreadDMData> 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<IDMContext> 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<Boolean> 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<MIInfo>(getExecutor(), rm));
|
||||
new DataRequestMonitor<MIInfo>(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<Boolean> rm) {
|
||||
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<MIListThreadGroupsInfo>(getExecutor(), rm) {
|
||||
|
@ -1067,13 +1327,34 @@ public class GDBProcesses_7_0 extends AbstractDsfService
|
|||
}
|
||||
});
|
||||
} else {
|
||||
|
||||
final DataRequestMonitor<IMIContainerDMContext[]> addExitedDRM =
|
||||
new ImmediateDataRequestMonitor<IMIContainerDMContext[]>(rm) {
|
||||
@Override
|
||||
protected void handleCompleted() {
|
||||
List<IMIContainerDMContext> 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<Entry<String, ExitedProcInfo>> entries = new ArrayList<>(getExitedProcesses().entrySet());
|
||||
for (int i = entries.size() - 1; i >= 0 ; i--) {
|
||||
Entry<String, ExitedProcInfo> 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<MIListThreadGroupsInfo>(getExecutor(), rm) {
|
||||
new DataRequestMonitor<MIListThreadGroupsInfo>(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) {
|
||||
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
|
||||
if (fBackend.getSessionType() == SessionType.CORE) {
|
||||
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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
@ -136,6 +137,12 @@ public class GDBProcesses_7_1 extends GDBProcesses_7_0 {
|
|||
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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
@ -510,6 +516,9 @@ public class GDBProcesses_7_2 extends GDBProcesses_7_1 implements IMultiTerminat
|
|||
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<MIInfo>(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<MIInfo>(getExecutor(), rm));
|
||||
new DataRequestMonitor<MIInfo>(getExecutor(), rm) {
|
||||
@Override
|
||||
protected void handleFailure() {
|
||||
// Detach failed
|
||||
getDetachedProcesses().remove(containerDmc.getGroupId());
|
||||
super.handleFailure();
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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<IContainerDMContext> 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) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<IThreadDMContext> {}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
|
|
@ -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) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue