1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-29 19:45:01 +02:00

Bug 422586 - Terminate session if remote connection is lost

Change-Id: I78e8ccd60c3f49e5c5ef87c5564cab8849281b8d
Signed-off-by: Marc Khouzam <marc.khouzam@ericsson.com>
Reviewed-on: https://git.eclipse.org/r/20286
Tested-by: Hudson CI
Reviewed-by: Mikhail Khodjaiants <mikhailkhod@googlemail.com>
IP-Clean: Mikhail Khodjaiants <mikhailkhod@googlemail.com>
Tested-by: Mikhail Khodjaiants <mikhailkhod@googlemail.com>
This commit is contained in:
Marc Khouzam 2014-01-03 15:55:27 -05:00
parent 78fc903d16
commit 1de1915f89

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2006, 2012 Wind River Systems and others.
* Copyright (c) 2006, 2014 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
@ -15,6 +15,7 @@
* Marc Khouzam (Ericsson) - Pass errorStream to startCommandProcessing() (Bug 350837)
* Mikhail Khodjaiants (Mentor Graphics) - Terminate should cancel the initialization sequence
* if it is still running (bug 373845)
* Marc Khouzam (Ericsson) - Terminate the session if we lose the connection to the remote target (bug 422586)
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.service.command;
@ -44,6 +45,7 @@ import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext;
import org.eclipse.cdt.dsf.debug.service.command.ICommandControl;
import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService;
import org.eclipse.cdt.dsf.debug.service.command.ICommandResult;
import org.eclipse.cdt.dsf.debug.service.command.ICommandToken;
import org.eclipse.cdt.dsf.gdb.IGdbDebugConstants;
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
@ -66,7 +68,15 @@ import org.eclipse.cdt.dsf.mi.service.command.CommandFactory;
import org.eclipse.cdt.dsf.mi.service.command.IEventProcessor;
import org.eclipse.cdt.dsf.mi.service.command.MIControlDMContext;
import org.eclipse.cdt.dsf.mi.service.command.MIRunControlEventProcessor;
import org.eclipse.cdt.dsf.mi.service.command.output.MIConsoleStreamOutput;
import org.eclipse.cdt.dsf.mi.service.command.output.MIConst;
import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo;
import org.eclipse.cdt.dsf.mi.service.command.output.MILogStreamOutput;
import org.eclipse.cdt.dsf.mi.service.command.output.MIOOBRecord;
import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput;
import org.eclipse.cdt.dsf.mi.service.command.output.MIResult;
import org.eclipse.cdt.dsf.mi.service.command.output.MIResultRecord;
import org.eclipse.cdt.dsf.mi.service.command.output.MIValue;
import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.core.runtime.CoreException;
@ -127,12 +137,61 @@ public class GDBControl extends AbstractMIControl implements IGDBControl {
}
}
/**
* An event processor that handles some GDB life cycle events.
* Currently, it detects a lost connection with the remote.
*/
private class ControlEventProcessor implements IEventProcessor {
public ControlEventProcessor() {
addCommandListener(this);
addEventListener(this);
}
@Override
public void dispose() {
removeEventListener(this);
removeCommandListener(this);
}
@Override
public void eventReceived(Object output) {
if (output instanceof MIOutput) {
verifyConnectionLost((MIOutput)output);
} else {
assert false;
}
}
@Override
public void commandDone(ICommandToken token, ICommandResult cmdResult) {
if (cmdResult instanceof MIInfo) {
verifyConnectionLost(((MIInfo)cmdResult).getMIOutput());
} else {
assert false;
}
}
@Override
public void commandQueued(ICommandToken token) {
}
@Override
public void commandSent(ICommandToken token) {
}
@Override
public void commandRemoved(ICommandToken token) {
}
}
private GDBControlDMContext fControlDmc;
private IGDBBackend fMIBackend;
private IEventProcessor fMIEventProcessor;
private IEventProcessor fCLICommandProcessor;
private IEventProcessor fControlEventProcessor;
private AbstractCLIProcess fCLIProcess;
private GdbCommandTimeoutManager fCommandTimeoutManager;
@ -325,7 +384,6 @@ public class GDBControl extends AbstractMIControl implements IGDBControl {
/**
* @since 4.0
*/
@SuppressWarnings("unchecked")
@Override
public void completeInitialization(final RequestMonitor rm) {
// We take the attributes from the launchConfiguration
@ -462,13 +520,15 @@ public class GDBControl extends AbstractMIControl implements IGDBControl {
fCLICommandProcessor = createCLIEventProcessor(GDBControl.this, fControlDmc);
fMIEventProcessor = createMIRunControlEventProcessor(GDBControl.this, fControlDmc);
fControlEventProcessor = createControlEventProcessor();
requestMonitor.done();
}
@Override
protected void shutdown(RequestMonitor requestMonitor) {
fCLICommandProcessor.dispose();
fControlEventProcessor.dispose();
fCLICommandProcessor.dispose();
fMIEventProcessor.dispose();
fCLIProcess.dispose();
@ -594,6 +654,11 @@ public class GDBControl extends AbstractMIControl implements IGDBControl {
return new MIRunControlEventProcessor(connection, controlDmc);
}
/** @since 4.3 */
protected IEventProcessor createControlEventProcessor() {
return new ControlEventProcessor();
}
/**
* @since 4.1
*/
@ -654,6 +719,105 @@ public class GDBControl extends AbstractMIControl implements IGDBControl {
}
}
/**
* Parse output from GDB to determine if the connection to the remote was lost.
*
* @param output The output received from GDB that must be parsed
* to determine if the connection to the remote was lost.
*
* @return True if the connection was lost, false otherwise.
* @since 4.3
*/
protected boolean verifyConnectionLost(MIOutput output) {
boolean connectionLost = false;
String reason = null;
// Check if any command has a result that indicates a lost connection.
// This can happen as a normal command result, or as an out-of-band event.
// The out-of-band case can happen when GDB sends another response to
// a previous command. This case can happen, for example, in all-stop
// when sending an -exec-continue and then killing gdbserver while connected
// to a process; in that case a second result to -exec-continue will be sent
// and will indicate the remote connection is closed.
MIResultRecord rr = output.getMIResultRecord();
if (rr != null) {
String state = rr.getResultClass();
if ("error".equals(state)) { //$NON-NLS-1$
MIResult[] results = rr.getMIResults();
for (MIResult result : results) {
String var = result.getVariable();
if (var.equals("msg")) { //$NON-NLS-1$
MIValue value = result.getMIValue();
if (value instanceof MIConst) {
String str = ((MIConst)value).getCString();
if (str != null && str.startsWith("Remote connection closed")) { //$NON-NLS-1$
connectionLost = true;
reason = str;
}
}
}
}
}
} else {
// Only check for out-of-band records when there is no result record.
// This is because OOBRecords that precede a result are included in the
// result output even though they have already been processed. To avoid
// processing them a second time, we only handle them when not dealing
// with a result record.
for (MIOOBRecord oobr : output.getMIOOBRecords()) {
if (oobr instanceof MIConsoleStreamOutput) {
MIConsoleStreamOutput out = (MIConsoleStreamOutput) oobr;
String str = out.getString();
if (str != null && str.startsWith("Ending remote debugging")) { //$NON-NLS-1$
// This happens if the user types 'disconnect' in the console
connectionLost = true;
reason = str;
break;
// Note that this will not trigger in the case where a
// -target-disconnect is used. This is a case that CDT handles
// explicitly as it is an MI command; we shouldn't have to catch it here.
// In fact, catching it here and terminating the session would break
// the workaround for GDB 7.2 handled by GDBProcesses_7_2.needFixForGDB72Bug352998()
}
} else if (oobr instanceof MILogStreamOutput) {
MILogStreamOutput out = (MILogStreamOutput) oobr;
String str = out.getString().trim();
if (str != null && str.startsWith("Remote connection closed")) { //$NON-NLS-1$
// This happens if gdbserver is killed or dies
connectionLost = true;
reason = str;
break;
}
}
}
}
if (connectionLost) {
connectionLost(reason);
return true;
}
return false;
}
/**
* Handle the loss of the connection to the remote.
* The default implementation terminates the debug session.
*
* @param reason A string indicating as much as possible why the connection was lost. Can be null.
* @since 4.3
*/
protected void connectionLost(String reason) {
terminate(new ImmediateRequestMonitor() {
@Override
protected void handleErrorOrWarning() {
GdbPlugin.getDefault().getLog().log(getStatus());
super.handleErrorOrWarning();
};
});
}
/**
* @since 4.1
*/