1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-07-24 01:15:29 +02:00

Bug 464056 - Wrong exit code returned in some multi-process cases

Change-Id: I71c3aca8c8c98494408d2a7677d2837768592b09
Signed-off-by: Marc Khouzam <marc.khouzam@ericsson.com>
This commit is contained in:
Marc Khouzam 2015-04-06 13:14:43 -04:00
parent ba5e0ca0f5
commit 0c2af8d5ce
7 changed files with 262 additions and 73 deletions

View file

@ -0,0 +1,39 @@
/*******************************************************************************
* Copyright (c) 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Marc Khouzam (Ericsson) - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.service;
import java.util.Map;
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.service.DsfSession;
/**
* Version for GDB 7.3
*
* @since 4.7
*/
public class GDBProcesses_7_3 extends GDBProcesses_7_2_1 {
public GDBProcesses_7_3(DsfSession session) {
super(session);
}
@Override
protected Sequence getStartOrRestartProcessSequence(DsfExecutor executor, IContainerDMContext containerDmc,
Map<String, Object> attributes, boolean restart,
DataRequestMonitor<IContainerDMContext> rm) {
return new StartOrRestartProcessSequence_7_3(executor, containerDmc, attributes, restart, rm);
}
}

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2014 Ericsson and others.
* Copyright (c) 2014, 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
@ -19,7 +19,7 @@ import org.eclipse.cdt.dsf.service.DsfSession;
*
* @since 4.4
*/
public class GDBProcesses_7_4 extends GDBProcesses_7_2_1 {
public class GDBProcesses_7_4 extends GDBProcesses_7_3 {
public GDBProcesses_7_4(DsfSession session) {
super(session);

View file

@ -224,6 +224,9 @@ public class GdbDebugServicesFactory extends AbstractDsfDebugServicesFactory {
if (GDB_7_4_VERSION.compareTo(fVersion) <= 0) {
return new GDBProcesses_7_4(session);
}
if (GDB_7_3_VERSION.compareTo(fVersion) <= 0) {
return new GDBProcesses_7_3(session);
}
if (GDB_7_2_1_VERSION.compareTo(fVersion) <= 0) {
return new GDBProcesses_7_2_1(session);
}

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2011, 2012 Ericsson and others.
* Copyright (c) 2011, 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,6 +13,7 @@
package org.eclipse.cdt.dsf.gdb.service;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
@ -291,6 +292,16 @@ public class StartOrRestartProcessSequence_7_0 extends ReflectionSequence {
}
}
/** @since 4.7 */
protected MIInferiorProcess createInferiorProcess(IContainerDMContext container, OutputStream outputStream) {
return new MIInferiorProcess(container, outputStream);
}
/** @since 4.7 */
protected MIInferiorProcess createInferiorProcess(IContainerDMContext container, PTY pty) {
return new MIInferiorProcess(container, pty);
}
/**
* Before running the program, we must create its console for IO.
*/
@ -305,9 +316,9 @@ public class StartOrRestartProcessSequence_7_0 extends ReflectionSequence {
Process inferiorProcess;
if (fPty == null) {
inferiorProcess = new MIInferiorProcess(fContainerDmc, fBackend.getMIOutputStream());
inferiorProcess = createInferiorProcess(fContainerDmc, fBackend.getMIOutputStream());
} else {
inferiorProcess = new MIInferiorProcess(fContainerDmc, fPty);
inferiorProcess = createInferiorProcess(fContainerDmc, fPty);
}
final Process inferior = inferiorProcess;

View file

@ -0,0 +1,43 @@
/*******************************************************************************
* Copyright (c) 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Marc Khouzam (Ericsson) - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.service;
import java.io.OutputStream;
import java.util.Map;
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.DsfExecutor;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext;
import org.eclipse.cdt.dsf.mi.service.command.MIInferiorProcess;
import org.eclipse.cdt.dsf.mi.service.command.MIInferiorProcess_7_3;
import org.eclipse.cdt.utils.pty.PTY;
/**
* Specialization for GDB >= 7.3
* @since 4.7
*/
public class StartOrRestartProcessSequence_7_3 extends StartOrRestartProcessSequence_7_0 {
public StartOrRestartProcessSequence_7_3(DsfExecutor executor, IContainerDMContext containerDmc,
Map<String, Object> attributes, boolean restart, DataRequestMonitor<IContainerDMContext> rm) {
super(executor, containerDmc, attributes, restart, rm);
}
@Override
protected MIInferiorProcess createInferiorProcess(IContainerDMContext container, OutputStream outputStream) {
return new MIInferiorProcess_7_3(container, outputStream);
}
@Override
protected MIInferiorProcess createInferiorProcess(IContainerDMContext container, PTY pty) {
return new MIInferiorProcess_7_3(container, pty);
}
}

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2009, 2013 QNX Software Systems and others.
* Copyright (c) 2009, 2015 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
@ -42,12 +42,9 @@ 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;
@ -61,13 +58,11 @@ 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
* by GDB. The process object is displayed in Debug view and is used to
* channel the STDIO of the interior process to the console view.
* This Process implementation tracks one of the inferiors that is being debugged
* by GDB. The process object, although not displayed in the Debug view, is used to
* channel the STDIO of the inferior process to the console view.
*
* @see org.eclipse.debug.core.model.IProcess
*/
@ -118,7 +113,28 @@ public class MIInferiorProcess extends Process
@ThreadSafe
Integer fExitCode = null;
/**
* @returns if that the inferior has been started.
* This is important for the case of a restart
* where we need to make sure not to terminate
* the new inferior, which was not started yet.
* @since 4.7
*/
protected boolean isStarted() {
return fStarted;
}
/** @since 4.7 */
protected IContainerDMContext getContainer() {
return fContainerDMContext;
}
/** @since 4.7 */
protected synchronized boolean isTerminated() {
return fTerminated;
}
/**
* Creates an inferior process object which uses the given output stream
* to write the user standard input into.
@ -145,8 +161,9 @@ public class MIInferiorProcess extends Process
this(container, null, p);
}
/** @since 4.7 */
@ConfinedToDsfExecutor("fSession#getExecutor")
private MIInferiorProcess(IContainerDMContext container, final OutputStream gdbOutputStream, PTY pty) {
protected MIInferiorProcess(IContainerDMContext container, final OutputStream gdbOutputStream, PTY pty) {
fSession = DsfSession.getSession(container.getSessionId());
fSession.addServiceEventListener(this, null);
@ -260,11 +277,13 @@ 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.
// Fetch the exit code using $_exitcode of GDB when doing single
// process debugging (GDB <= 7.1)
// Note that for GDB 7.2, there is no proper solution for the exit code
// because although we support multi-process, $_exitcode was still the
// only way to get the exit code, and that variable does not work properly
// with multi-process (it is re-used by the different processes).
// We use it still for GDB 7.2, since the single-process case is the most common.
try {
Query<Integer> exitCodeQuery = new Query<Integer>() {
@Override
@ -282,7 +301,7 @@ public class MIInferiorProcess extends Process
} else if (!fTerminated) {
// This will cause ExecutionException to be thrown with a CoreException,
// which will in turn contain the IllegalThreadStateException.
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, "GDB is still running.", new IllegalThreadStateException())); //$NON-NLS-1$
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, "Inferior is still running.", new IllegalThreadStateException())); //$NON-NLS-1$
rm.done();
} else {
// The exitCode from GDB does not seem to be handled for multi-process
@ -461,28 +480,6 @@ 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) {
}
}
}
}
}
}
/**
@ -525,34 +522,4 @@ 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;
}
}
}
}
}
}

View file

@ -0,0 +1,126 @@
/*******************************************************************************
* Copyright (c) 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Marc Khouzam (Ericsson) - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.dsf.mi.service.command;
import java.io.OutputStream;
import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor;
import org.eclipse.cdt.dsf.concurrent.ThreadSafeAndProhibitedFromDsfExecutor;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext;
import org.eclipse.cdt.dsf.gdb.IGdbDebugConstants;
import org.eclipse.cdt.dsf.gdb.launching.InferiorRuntimeProcess;
import org.eclipse.cdt.dsf.mi.service.IMIContainerDMContext;
import org.eclipse.cdt.dsf.mi.service.command.events.MIThreadGroupExitedEvent;
import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.cdt.utils.pty.PTY;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.model.IProcess;
/**
* Specialization for GDB >= 7.3.
*
* @since 4.7
*/
public class MIInferiorProcess_7_3 extends MIInferiorProcess
{
private DsfSession fSession;
@ConfinedToDsfExecutor("fSession#getExecutor")
public MIInferiorProcess_7_3(IContainerDMContext container, OutputStream gdbOutputStream) {
this(container, gdbOutputStream, null);
}
@ConfinedToDsfExecutor("fSession#getExecutor")
public MIInferiorProcess_7_3(IContainerDMContext container, PTY p) {
this(container, null, p);
}
@ConfinedToDsfExecutor("fSession#getExecutor")
protected MIInferiorProcess_7_3(IContainerDMContext container, final OutputStream gdbOutputStream, PTY pty) {
super(container, gdbOutputStream, pty);
fSession = DsfSession.getSession(container.getSessionId());
}
@ThreadSafeAndProhibitedFromDsfExecutor("fSession#getExecutor")
@Override
public int exitValue() {
assert !fSession.getExecutor().isInExecutorThread();
synchronized(this) {
if (fExitCode != null) {
return fExitCode;
}
}
if (!isTerminated()) {
// Throw an exception because the process is still running.
throw new IllegalThreadStateException();
}
return 0;
}
/** @since 4.2 */
@Override
@DsfServiceEventHandler
public void eventDispatched(MIThreadGroupExitedEvent e) {
if (getContainer() instanceof IMIContainerDMContext) {
if (((IMIContainerDMContext)getContainer()).getGroupId().equals(e.getGroupId())) {
if (isStarted()) {
// 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) {
}
}
}
}
}
}
/**
* 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) {
String groupAttribute = proc.getAttribute(IGdbDebugConstants.INFERIOR_GROUPID_ATTR);
if (getContainer() instanceof IMIContainerDMContext) {
if (groupAttribute != null && groupAttribute.equals(((IMIContainerDMContext)getContainer()).getGroupId())) {
// Simply set the attribute that indicates the inferior has properly exited and its
// exit code can be used.
proc.setAttribute(IGdbDebugConstants.INFERIOR_EXITED_ATTR, ""); //$NON-NLS-1$
return;
}
}
}
}
}
}