diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/IGdbDebugConstants.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/IGdbDebugConstants.java index a56582932cf..7d036a25f0b 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/IGdbDebugConstants.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/IGdbDebugConstants.java @@ -58,6 +58,13 @@ public interface IGdbDebugConstants { * @since 4.1 */ public static final String GDB_PROCESS_CREATION_VALUE = PREFIX + "gdbProcess"; //$NON-NLS-1$ - + + /** + * Attribute key that when set, indicates that the inferior process has properly exited, + * and its exit value can be used. + * @since 4.2 + */ + public static final String INFERIOR_EXITED_ATTR = PREFIX + "inferiorExited"; //$NON-NLS-1$ + } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/InferiorRuntimeProcess.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/InferiorRuntimeProcess.java index 0e05153678e..ab51664f54e 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/InferiorRuntimeProcess.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/InferiorRuntimeProcess.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2011 Ericsson and others. + * Copyright (c) 2011, 2013 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 @@ -7,24 +7,73 @@ * * Contributors: * Ericsson - Initial API and implementation + * Marc Khouzam (Ericsson) - Display exit code in process console (Bug 402054) *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.launching; import java.util.Map; +import org.eclipse.cdt.dsf.gdb.IGdbDebugConstants; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.model.IProcess; import org.eclipse.debug.core.model.RuntimeProcess; +import com.ibm.icu.text.MessageFormat; + /** * A process for the inferior to know it belongs to a DSF-GDB session * * @since 4.0 */ public class InferiorRuntimeProcess extends RuntimeProcess { - + public InferiorRuntimeProcess(ILaunch launch, Process process, String name, Map attributes) { super(launch, process, name, attributes); } + + @Override + protected void terminated() { + super.terminated(); + setConsoleTerminatedLabel(); + } + + // Inspired from org.eclipse.debug.internal.ui.views.console.ProcessConsole#computeName + // We set the IProcess.ATTR_PROCESS_LABEL to modify the console title but not the process label + // of the debug view. Overriding getLabel() affects the element in the debug view also, so + // we don't do that. + private void setConsoleTerminatedLabel() { + String label = getLabel(); + String type = null; + ILaunchConfiguration config = getLaunch().getLaunchConfiguration(); + try { + type = config.getType().getName(); + } catch (CoreException e) { + } + StringBuffer buffer = new StringBuffer(); + buffer.append(config.getName()); + if (type != null) { + buffer.append(" ["); //$NON-NLS-1$ + buffer.append(type); + buffer.append("] "); //$NON-NLS-1$ + } + buffer.append(label); + + if (getAttribute(IGdbDebugConstants.INFERIOR_EXITED_ATTR) != null) { + // Add the exit code to the label if the inferior properly exited. + try { + buffer.insert(0, MessageFormat.format(LaunchMessages.getString("InferiorRuntimeProcess_ExitValue"), //$NON-NLS-1$ + new Object[] { getExitValue() })); + } catch (DebugException e) { + // Process not terminated. Should not happen. But even so, we should use the plain label. + } + } + label = buffer.toString(); + + setAttribute(IProcess.ATTR_PROCESS_LABEL, label); + } } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/LaunchMessages.properties b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/LaunchMessages.properties index c1fb106ee40..f3ac821b8f4 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/LaunchMessages.properties +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/LaunchMessages.properties @@ -176,3 +176,5 @@ LocalCDILaunchDelegate.9=Eclipse runtime does not support working directory. LocalCDILaunchDelegate.10=Failed to set program arguments, environment or working directory. ServicesLaunchSequence_0=Initializing debugger services ServicesLaunchSequence_1=Aborting debugger services initialization + +InferiorRuntimeProcess_ExitValue=(exit value: {0}) diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/MIInferiorProcess.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/MIInferiorProcess.java index ad8319ada7c..970255b3f92 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/MIInferiorProcess.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/MIInferiorProcess.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2009, 2011 QNX Software Systems and others. + * Copyright (c) 2009, 2013 QNX Software Systems and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -9,6 +9,7 @@ * QNX Software Systems - Initial API and implementation * Hewlett-Packard Development Company - fix for bug 109733 * Wind River Systems - Modified for new DSF Reference Implementation + * Marc Khouzam (Ericsson) - Display exit code in process console (Bug 402054) *******************************************************************************/ package org.eclipse.cdt.dsf.mi.service.command; @@ -41,10 +42,14 @@ import org.eclipse.cdt.dsf.debug.service.command.ICommandListener; import org.eclipse.cdt.dsf.debug.service.command.ICommandResult; import org.eclipse.cdt.dsf.debug.service.command.ICommandToken; import org.eclipse.cdt.dsf.debug.service.command.IEventListener; +import org.eclipse.cdt.dsf.gdb.IGdbDebugConstants; import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.gdb.launching.InferiorRuntimeProcess; import org.eclipse.cdt.dsf.mi.service.IMICommandControl; import org.eclipse.cdt.dsf.mi.service.IMIContainerDMContext; +import org.eclipse.cdt.dsf.mi.service.MIProcesses; import org.eclipse.cdt.dsf.mi.service.command.commands.CLICommand; +import org.eclipse.cdt.dsf.mi.service.command.events.MIThreadGroupExitedEvent; import org.eclipse.cdt.dsf.mi.service.command.output.MIGDBShowExitCodeInfo; import org.eclipse.cdt.dsf.mi.service.command.output.MIOOBRecord; import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput; @@ -56,6 +61,8 @@ import org.eclipse.cdt.utils.pty.PTY; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.model.IProcess; /** * This Process implementation tracks the process that is being debugged @@ -253,6 +260,11 @@ public class MIInferiorProcess extends Process } } + // The below should only be used for GDB < 7.3 because $_exitcode does not + // support multi-process properly. Note that for GDB 7.2, there is no proper + // solution because although we support multi-process, $_exitcode was still the + // only way to get the exit code. For GDB >= 7.3, we can use =thread-group-exited + // which provides the exit code; in fact, fExitCode will already be set for that case. try { Query exitCodeQuery = new Query() { @Override @@ -368,7 +380,7 @@ public class MIInferiorProcess extends Process setTerminated(); } } - + @ConfinedToDsfExecutor("fSession#getExecutor") private synchronized void setTerminated() { if (fTerminated) return; @@ -446,6 +458,33 @@ public class MIInferiorProcess extends Process } } + /** @since 4.2 */ + @DsfServiceEventHandler + public void eventDispatched(MIThreadGroupExitedEvent e) { + if (fContainerDMContext instanceof IMIContainerDMContext) { + if (((IMIContainerDMContext)fContainerDMContext).getGroupId().equals(e.getGroupId())) { + if (fStarted) { + // Only handle this event if this process was already + // started. This is to protect ourselves in the case of + // a restart, where the new inferior is already created + // and gets the exited event for the old inferior. + String exitCode = e.getExitCode(); + if (exitCode != null) { + setExitCodeAttribute(); + try { + // Must use 'decode' since GDB returns an octal value + Integer decodedExitCode = Integer.decode(exitCode); + synchronized(this) { + fExitCode = decodedExitCode; + } + } catch (NumberFormatException exception) { + } + } + } + } + } + } + /** * @since 4.0 */ @@ -486,4 +525,34 @@ public class MIInferiorProcess extends Process public void eventDispatched(ICommandControlShutdownDMEvent e) { dispose(); } + + /** + * Set an attribute in the inferior process of the launch to indicate + * that the inferior has properly exited and its exit value can be used. + */ + @ConfinedToDsfExecutor("fSession#getExecutor") + private void setExitCodeAttribute() { + // Update the console label to contain the exit code + ILaunch launch = (ILaunch)fSession.getModelAdapter(ILaunch.class); + IProcess[] launchProcesses = launch.getProcesses(); + for (IProcess proc : launchProcesses) { + if (proc instanceof InferiorRuntimeProcess) { + InferiorRuntimeProcess process = (InferiorRuntimeProcess)proc; + String groupAttribute = process.getAttribute(IGdbDebugConstants.INFERIOR_GROUPID_ATTR); + + // if the groupAttribute is not set in the process we know we are dealing + // with single process debugging so the one process is the one we want. + // If the groupAttribute is set, then we must make sure it is the proper inferior + if (fContainerDMContext instanceof IMIContainerDMContext) { + if (groupAttribute == null || groupAttribute.equals(MIProcesses.UNIQUE_GROUP_ID) || + groupAttribute.equals(((IMIContainerDMContext)fContainerDMContext).getGroupId())) { + // Simply set the attribute that indicates the inferior has properly exited and its + // exit code can be used. + process.setAttribute(IGdbDebugConstants.INFERIOR_EXITED_ATTR, ""); //$NON-NLS-1$ + return; + } + } + } + } + } } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/MIRunControlEventProcessor_7_0.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/MIRunControlEventProcessor_7_0.java index beac6f22d4e..791e635107e 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/MIRunControlEventProcessor_7_0.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/MIRunControlEventProcessor_7_0.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006, 2010 Wind River Systems and others. + * Copyright (c) 2006, 2013 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 * Ericsson - Version 7.0 * Mikhail Khodjaiants (Mentor Graphics) - Refactor common code in GDBControl* classes (bug 372795) + * Marc Khouzam (Ericsson) - Display exit code in process console (Bug 402054) *******************************************************************************/ package org.eclipse.cdt.dsf.mi.service.command; @@ -264,7 +265,7 @@ public class MIRunControlEventProcessor_7_0 IContainerDMContext containerDmc = procService.createContainerContextFromGroupId(fControlDmc, groupId); IProcessDMContext procDmc = DMContexts.getAncestorOfType(containerDmc, IProcessDMContext.class); - MIEvent event = new MIThreadGroupExitedEvent(procDmc, exec.getToken(), groupId); + MIEvent event = new MIThreadGroupExitedEvent(procDmc, exec.getToken(), exec.getMIResults()); fCommandControl.getSession().dispatchEvent(event, fCommandControl.getProperties()); } } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIGDBShowExitCode.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIGDBShowExitCode.java index 7c7efac62b9..c0f5bae32b9 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIGDBShowExitCode.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIGDBShowExitCode.java @@ -22,6 +22,9 @@ import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput; * ^done,value="10" * * Show the current value of a $_exitcode + * + * With GDB 7.3, the exit code is provided by the MI =thread-group-exited event, + * which allows to handle multi-process situations. * */ public class MIGDBShowExitCode extends MIDataEvaluateExpression { diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIThreadGroupExitedEvent.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIThreadGroupExitedEvent.java index 8f64ac45007..d698bc8a184 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIThreadGroupExitedEvent.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIThreadGroupExitedEvent.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008, 2009 Ericsson and others. + * Copyright (c) 2008, 2013 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 @@ -7,28 +7,73 @@ * * Contributors: * Ericsson - Initial API and implementation + * Marc Khouzam (Ericsson) - Display exit code in process console (Bug 402054) *******************************************************************************/ package org.eclipse.cdt.dsf.mi.service.command.events; import org.eclipse.cdt.dsf.concurrent.Immutable; import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIConst; +import org.eclipse.cdt.dsf.mi.service.command.output.MIResult; +import org.eclipse.cdt.dsf.mi.service.command.output.MIValue; /** - * This can only be detected by gdb/mi after GDB 6.8. + * This can only be detected by gdb/mi starting with GDB 7.0. * @since 1.1 - * */ @Immutable public class MIThreadGroupExitedEvent extends MIEvent { - final private String fGroupId; + private String fGroupId; + private String fExitCode; + /** + * @deprecated + */ + @Deprecated public MIThreadGroupExitedEvent(IProcessDMContext ctx, int token, String groupId) { super(ctx, token, null); fGroupId = groupId; + fExitCode = null; + } + + /** @since 4.2 */ + public MIThreadGroupExitedEvent(IProcessDMContext ctx, int token, MIResult[] results) { + super(ctx, token, results); + parse(); } public String getGroupId() { return fGroupId; } + + /** + * Returns the exit code of the process or null if there is no exit code. + * Note that this information is only available with GDB 7.3; + * null will be returned for older GDB versions. + * + * @since 4.2 + */ + public String getExitCode() { return fExitCode; } + + private void parse() { + MIResult[] results = getResults(); + if (results == null) return; + + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + MIValue val = results[i].getMIValue(); + if (var.equals("id")) { //$NON-NLS-1$ + if (val instanceof MIConst) { + fGroupId = ((MIConst) val).getString().trim(); + } + } else if (var.equals("exit-code")) { //$NON-NLS-1$ + // Available starting with GDB 7.3. + // Only present when the process properly exited + if (val instanceof MIConst) { + fExitCode = ((MIConst) val).getString().trim(); + } + } + } + } }