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:
parent
78fc903d16
commit
1de1915f89
1 changed files with 167 additions and 3 deletions
|
@ -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
|
* All rights reserved. This program and the accompanying materials
|
||||||
* are made available under the terms of the Eclipse Public License v1.0
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
* which accompanies this distribution, and is available at
|
* which accompanies this distribution, and is available at
|
||||||
|
@ -15,6 +15,7 @@
|
||||||
* Marc Khouzam (Ericsson) - Pass errorStream to startCommandProcessing() (Bug 350837)
|
* Marc Khouzam (Ericsson) - Pass errorStream to startCommandProcessing() (Bug 350837)
|
||||||
* Mikhail Khodjaiants (Mentor Graphics) - Terminate should cancel the initialization sequence
|
* Mikhail Khodjaiants (Mentor Graphics) - Terminate should cancel the initialization sequence
|
||||||
* if it is still running (bug 373845)
|
* 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;
|
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.IRunControl.IContainerDMContext;
|
||||||
import org.eclipse.cdt.dsf.debug.service.command.ICommandControl;
|
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.ICommandControlService;
|
||||||
|
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.ICommandToken;
|
||||||
import org.eclipse.cdt.dsf.gdb.IGdbDebugConstants;
|
import org.eclipse.cdt.dsf.gdb.IGdbDebugConstants;
|
||||||
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
|
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.IEventProcessor;
|
||||||
import org.eclipse.cdt.dsf.mi.service.command.MIControlDMContext;
|
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.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.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.DsfServiceEventHandler;
|
||||||
import org.eclipse.cdt.dsf.service.DsfSession;
|
import org.eclipse.cdt.dsf.service.DsfSession;
|
||||||
import org.eclipse.core.runtime.CoreException;
|
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 GDBControlDMContext fControlDmc;
|
||||||
|
|
||||||
private IGDBBackend fMIBackend;
|
private IGDBBackend fMIBackend;
|
||||||
|
|
||||||
private IEventProcessor fMIEventProcessor;
|
private IEventProcessor fMIEventProcessor;
|
||||||
private IEventProcessor fCLICommandProcessor;
|
private IEventProcessor fCLICommandProcessor;
|
||||||
|
private IEventProcessor fControlEventProcessor;
|
||||||
private AbstractCLIProcess fCLIProcess;
|
private AbstractCLIProcess fCLIProcess;
|
||||||
|
|
||||||
private GdbCommandTimeoutManager fCommandTimeoutManager;
|
private GdbCommandTimeoutManager fCommandTimeoutManager;
|
||||||
|
@ -325,7 +384,6 @@ public class GDBControl extends AbstractMIControl implements IGDBControl {
|
||||||
/**
|
/**
|
||||||
* @since 4.0
|
* @since 4.0
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
@Override
|
||||||
public void completeInitialization(final RequestMonitor rm) {
|
public void completeInitialization(final RequestMonitor rm) {
|
||||||
// We take the attributes from the launchConfiguration
|
// We take the attributes from the launchConfiguration
|
||||||
|
@ -462,13 +520,15 @@ public class GDBControl extends AbstractMIControl implements IGDBControl {
|
||||||
|
|
||||||
fCLICommandProcessor = createCLIEventProcessor(GDBControl.this, fControlDmc);
|
fCLICommandProcessor = createCLIEventProcessor(GDBControl.this, fControlDmc);
|
||||||
fMIEventProcessor = createMIRunControlEventProcessor(GDBControl.this, fControlDmc);
|
fMIEventProcessor = createMIRunControlEventProcessor(GDBControl.this, fControlDmc);
|
||||||
|
fControlEventProcessor = createControlEventProcessor();
|
||||||
|
|
||||||
requestMonitor.done();
|
requestMonitor.done();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void shutdown(RequestMonitor requestMonitor) {
|
protected void shutdown(RequestMonitor requestMonitor) {
|
||||||
fCLICommandProcessor.dispose();
|
fControlEventProcessor.dispose();
|
||||||
|
fCLICommandProcessor.dispose();
|
||||||
fMIEventProcessor.dispose();
|
fMIEventProcessor.dispose();
|
||||||
fCLIProcess.dispose();
|
fCLIProcess.dispose();
|
||||||
|
|
||||||
|
@ -594,6 +654,11 @@ public class GDBControl extends AbstractMIControl implements IGDBControl {
|
||||||
return new MIRunControlEventProcessor(connection, controlDmc);
|
return new MIRunControlEventProcessor(connection, controlDmc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 4.3 */
|
||||||
|
protected IEventProcessor createControlEventProcessor() {
|
||||||
|
return new ControlEventProcessor();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @since 4.1
|
* @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
|
* @since 4.1
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Add table
Reference in a new issue