mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-04-23 22:52:11 +02:00
Bug 304096: Interrupting a Windows target does not always work, revisited. CDI adjustments.
This commit is contained in:
parent
8f44d67a49
commit
a67d730090
5 changed files with 187 additions and 35 deletions
|
@ -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.
|
||||
*
|
||||
* <ul>
|
||||
* <li>If the process this is being raised against was launched by us (the Spawner)
|
||||
* <ul>
|
||||
* <li>If the process is a cygwin program (has the cygwin1.dll loaded), then issue a 'kill -SIGINT'. If
|
||||
* the 'kill' utility isn't available, send the process a CTRL-C
|
||||
* <li>If the process is <i>not</i> a cygwin program, send the process a CTRL-C
|
||||
* </ul>
|
||||
* <li>If the process this is being raised against was <i>not</i> launched by us, search for a console
|
||||
* window whose PID matches the one we're trying to raise a signal on. If we find one, then send keyboard
|
||||
* events to simulate the user hitting CTRL-C at that console. We do all this because we can't programatically
|
||||
* send a CTRL-C to a process that doesn't share our console.
|
||||
* </ul>
|
||||
*
|
||||
* On non-Windows, raising this just raises a POSIX SIGINT
|
||||
*
|
||||
*/
|
||||
public int INT = 2;
|
||||
|
||||
/**
|
||||
* A fabricated signal number for use on Windows only. Tells the starter program to send a CTRL-C
|
||||
* regardless of whether the process is a Cygwin one or not.
|
||||
*
|
||||
* @since 5.2
|
||||
*/
|
||||
|
@ -212,20 +232,17 @@ public class Spawner extends Process {
|
|||
}
|
||||
|
||||
/**
|
||||
* Our extensions.
|
||||
**/
|
||||
* On Windows, interrupt the spawned program by using Cygwin's utility 'kill -SIGINT' if it's a Cgywin
|
||||
* program, otherwise send it a CTRL-C. If Cygwin's 'kill' command is not available, send a CTRL-C. On
|
||||
* linux, interrupt it by raising a SIGINT.
|
||||
*/
|
||||
public int interrupt() {
|
||||
return raise(pid, INT);
|
||||
}
|
||||
|
||||
/**
|
||||
* On Windows, this differs from interrupt() in that it will always send a CTRL-C event to the inferior,
|
||||
* whereas {@link #interrupt()} will do a Cygwin 'kill -SIGINT' if the inferior is a Cygwin process
|
||||
* (otherwise it sends a CTRL-C). In some cases, CDT needs to send CTRL-C regardless of whether the
|
||||
* inferior is a cygwin process or not, e.g., when interrupting gdb on Windows (see bug 304096).
|
||||
*
|
||||
* <p>
|
||||
* 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
|
||||
*/
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue