diff --git a/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/spawner/Spawner.java b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/spawner/Spawner.java index 2e40f46540e..41abe03670f 100644 --- a/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/spawner/Spawner.java +++ b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/spawner/Spawner.java @@ -27,14 +27,34 @@ public class Spawner extends Process { public int NOOP = 0; public int HUP = 1; - public int INT = 2; public int KILL = 9; public int TERM = 15; /** - * A fabricated signal number for use on Windows only. Tells the starter program to send a CTRL-C to the - * inferior regardless of whether the inferior is a Cygiwn process or not. With {@link #INT}, the starter - * does a 'kill -SIGINT' if the inferior is a Cygwin process. + * On Windows, what this does is far from easy to explain. + * Some of the logic is in the JNI code, some in the spawner.exe code. + * + *
- * On non-Windows hosts, this simply calls {@link #interrupt()} + * On Windows, interrupt the spawned program by send it a CTRL-C (even if it's a Cygwin program). On + * linux, interrupt it by raising a SIGINT. * * @since 5.2 */ diff --git a/debug/org.eclipse.cdt.debug.mi.core/mi/org/eclipse/cdt/debug/mi/core/MIInferior.java b/debug/org.eclipse.cdt.debug.mi.core/mi/org/eclipse/cdt/debug/mi/core/MIInferior.java index 4bb845afa7a..4a91e16a1e9 100644 --- a/debug/org.eclipse.cdt.debug.mi.core/mi/org/eclipse/cdt/debug/mi/core/MIInferior.java +++ b/debug/org.eclipse.cdt.debug.mi.core/mi/org/eclipse/cdt/debug/mi/core/MIInferior.java @@ -57,7 +57,7 @@ public class MIInferior extends Process { int inferiorPID; - /** See {@link #getIsRemoteInferior()} */ + /** See {@link #isRemoteInferior()} */ private boolean fIsRemoteInferior; public MIInferior(MISession mi, IMITTY p) { @@ -394,11 +394,24 @@ public class MIInferior extends Process { } /** - * Is the inferior process being debugged remotely through gdbserver? + * Is the inferior process being debugged remotely through gdbserver? This + * is mutually exclusive with {@link #isAttachedLocalInferior()}. * * @since 7.0 */ - public boolean getIsRemoteInferior() { + public boolean isRemoteInferior() { return fIsRemoteInferior; } + + /** + * Is this inferior process being debugged through a local attach session? + * I.e., is it a process running on "this" machine that was not launched by + * gdb but which gdb attached to? This is mutually exclusive with + * {@link #isRemoteInferior() + * + * @since 7.0 + */ + public boolean isAttachedLocalInferior() { + return session.isAttachSession(); + } } diff --git a/debug/org.eclipse.cdt.debug.mi.core/mi/org/eclipse/cdt/debug/mi/core/command/factories/win32/CygwinCommandFactory.java b/debug/org.eclipse.cdt.debug.mi.core/mi/org/eclipse/cdt/debug/mi/core/command/factories/win32/CygwinCommandFactory.java index 5b4897c997d..12b95acd18d 100644 --- a/debug/org.eclipse.cdt.debug.mi.core/mi/org/eclipse/cdt/debug/mi/core/command/factories/win32/CygwinCommandFactory.java +++ b/debug/org.eclipse.cdt.debug.mi.core/mi/org/eclipse/cdt/debug/mi/core/command/factories/win32/CygwinCommandFactory.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2004, 2010 QNX Software Systems and others. + * Copyright (c) 2004, 2007 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 @@ -10,7 +10,12 @@ *******************************************************************************/ package org.eclipse.cdt.debug.mi.core.command.factories.win32; +import java.io.IOException; + +import org.eclipse.cdt.debug.mi.core.CygwinMIProcessAdapter; +import org.eclipse.cdt.debug.mi.core.MIProcess; import org.eclipse.cdt.debug.mi.core.command.MIEnvironmentDirectory; +import org.eclipse.core.runtime.IProgressMonitor; /** * Command factory for the gdb/mi protocol for CygWin environment. @@ -34,4 +39,9 @@ public class CygwinCommandFactory extends StandardWinCommandFactory { public MIEnvironmentDirectory createMIEnvironmentDirectory(boolean reset, String[] pathdirs) { return new CygwinMIEnvironmentDirectory( getMIVersion(), reset, pathdirs ); } + + public MIProcess createMIProcess(String[] args, int launchTimeout, + IProgressMonitor monitor) throws IOException { + return new CygwinMIProcessAdapter(args, launchTimeout, monitor); + } } diff --git a/debug/org.eclipse.cdt.debug.mi.core/src/org/eclipse/cdt/debug/mi/core/CygwinMIProcessAdapter.java b/debug/org.eclipse.cdt.debug.mi.core/src/org/eclipse/cdt/debug/mi/core/CygwinMIProcessAdapter.java new file mode 100644 index 00000000000..5308c3e2188 --- /dev/null +++ b/debug/org.eclipse.cdt.debug.mi.core/src/org/eclipse/cdt/debug/mi/core/CygwinMIProcessAdapter.java @@ -0,0 +1,123 @@ +/********************************************************************** + * Copyright (c) 2007 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 + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - Initial API and implementation + **********************************************************************/ + +package org.eclipse.cdt.debug.mi.core; + +import java.io.IOException; + +import org.eclipse.cdt.utils.spawner.Spawner; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.Platform; + +/** + * @author Doug Schaefer + */ +public class CygwinMIProcessAdapter extends MIProcessAdapter { + + /** + * @param args + * @param launchTimeout + * @param monitor + * @throws IOException + */ + public CygwinMIProcessAdapter(String[] args, int launchTimeout, + IProgressMonitor monitor) throws IOException { + super(args, launchTimeout, monitor); + } + + public void interrupt(MIInferior inferior) { + if (fGDBProcess instanceof Spawner) { + if (inferior.isRunning()) { + boolean interruptedInferior = false; + Spawner gdbSpawner = (Spawner) fGDBProcess; + + // Cygwin gdb 6.8 is capricious when it comes to interrupting + // the target. MinGW and later versions of Cygwin aren't. A + // simple CTRL-C to gdb seems to do the trick in every case. + // Once we drop support for gdb 6.8, we should be able to ditch + // this method and rely on the base implementation + // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=304096#c56 + if (Platform.getOS().equals(Platform.OS_WIN32)) { + if (inferior.isRemoteInferior()) { + // Interrupt gdb with a 'kill -SIGINT'. The reason we + // need to send a simulated Cygwin/POSIX SIGINT to + // Cygwin gdb is that it has special handling in the case + // of remote debugging, as explained in the bugzilla + // comment above. That special handling will forward the + // interrupt request through gdbserver to the remote + // inferior, but the interrupt to gdb *must* be a + // simulated Cygwin/POSIX SIGINT; a CTRL-C won't do. + gdbSpawner.interrupt(); + } + else if (inferior.isAttachedLocalInferior()) { + // Cygwin gdb 6.8 has no support for forwarding an + // interrupt request to the local process it has + // attached to. That support has since been added and + // will be available in 7.x. So, the only way to suspend the + // attached-to inferior is to interrupt it directly. + // The following call will take a special path in the + // JNI code. See Java_org_eclipse_cdt_utils_spawner_Spawner_raise() + // We don't use the Cygwin 'kill' command since we don't + // know if the process associated with PID (the + // inferior) is a cygwin + // process (kill only works on cygwin programs). We also + // can't use GenerateConsoleCtrlEvent() to send a CTRL-C + // since that can only be used if the recipient shares a + // console with the caller. So, we end up looking for a + // console window associated with PID, and then we + // fabricate keyboard events to simulate the user doing + // a CTRL-C in that console! Crazy stuff, but it works. + // Thing is, the PID associated with the console window + // has to be that of the process we're trying to + // interrupt. What that means is that in order for CDT's + // 'suspend' button to work in an attach debug session, + // the inferior must have been launched with its own + // console. If you open a Windows console and type + // 'myprogram.exe', CDT can attach to it but not suspend + // it once it resumes it. Instead, you have to launch + // the program by using 'start myprogram.exe'. + interruptInferior(inferior); + interruptedInferior = true; + } + else { + // The typical case--gdb launches the inferior. + // Interrupt gdb but with a CTRL-C. gdb (6.8) itself + // doesn't have a handler for CTRL-C, but all processes + // in the console + // process group will receive the CTRL-C, and gdb + // registers itself to catch any such events that + // happen in the inferior. Thus it is able to determine + // and report that the inferior has been interrupted. + // But it's important we don't interrupt Cygwin gdb with + // a 'kill' since that will only reach gdb, and gdb + // won't forward the request on to the inferior. See + // bugzilla comment referenced above for details. + gdbSpawner.interruptCTRLC(); + } + } + else { + gdbSpawner.interrupt(); + } + + waitForInterrupt(inferior); + + // If we are still running try to interrupt the inferior (unless we + // already tried that above) + if (inferior.isRunning() && inferior.getInferiorPID() > 0 && !interruptedInferior) { + // lets try something else. + interruptInferior(inferior); + } + } + } + + } + +} diff --git a/debug/org.eclipse.cdt.debug.mi.core/src/org/eclipse/cdt/debug/mi/core/MIProcessAdapter.java b/debug/org.eclipse.cdt.debug.mi.core/src/org/eclipse/cdt/debug/mi/core/MIProcessAdapter.java index 7c146c4f832..9c803f90bdd 100644 --- a/debug/org.eclipse.cdt.debug.mi.core/src/org/eclipse/cdt/debug/mi/core/MIProcessAdapter.java +++ b/debug/org.eclipse.cdt.debug.mi.core/src/org/eclipse/cdt/debug/mi/core/MIProcessAdapter.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2010 QNX Software Systems and others. + * Copyright (c) 2000, 2009 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 @@ -123,28 +123,18 @@ public class MIProcessAdapter implements MIProcess { if (fGDBProcess instanceof Spawner) { if (inferior.isRunning()) { Spawner gdbSpawner = (Spawner) fGDBProcess; - - // Cygwin gdb 6.8 is capricious when it comes to interrupting the - // target. The same logic here will work with MinGW, though. And on - // linux it's irrelevant since interruptCTRLC()==interrupt(). So, - // one odd size fits all. - // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=304096#c54 - if (inferior.getIsRemoteInferior()) { - gdbSpawner.interrupt(); + if (inferior.isAttachedLocalInferior()) { + // not all gdb versions forward the interrupt to an attached + // inferior, so interrupt the inferior directly + interruptInferior(inferior); } else { - gdbSpawner.interruptCTRLC(); + // standard case (gdb launches process) and remote case (gdbserver) + gdbSpawner.interrupt(); } - waitForInterrupt(inferior); } - // If we are still running try to drop the sig to the PID - if (inferior.isRunning() && inferior.getInferiorPID() > 0) { - // lets try something else. - interruptInferior(inferior); - } } - } protected boolean waitForInterrupt(MIInferior inferior) { @@ -170,7 +160,6 @@ public class MIProcessAdapter implements MIProcess { if (fGDBProcess instanceof Spawner) { Spawner gdbSpawner = (Spawner) fGDBProcess; gdbSpawner.raise(inferior.getInferiorPID(), gdbSpawner.INT); - waitForInterrupt(inferior); } }