1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-08-03 14:25:37 +02:00

[240092] - [launch] The way to launch & configure GDB process in DSF is not customizable

This commit is contained in:
Pawel Piech 2008-10-14 17:31:52 +00:00
parent bb30f026fc
commit d18ba19313
34 changed files with 1584 additions and 1011 deletions

View file

@ -66,7 +66,7 @@ public class PDAVirtualMachineVMNode extends AbstractContainerVMNode
return;
}
update.setChild(createVMContext(commandControl.getVirtualMachineDMContext()), 0);
update.setChild(createVMContext(commandControl.getContext()), 0);
update.done();
}

View file

@ -88,7 +88,7 @@ implements ITerminate
* synchronization issues.
*/
@ConfinedToDsfExecutor("getSession().getExecutor()")
public void initializeServices(String program, int requestPort, int eventPort, final RequestMonitor rm)
public void initializeServices(String program, final RequestMonitor rm)
{
// Double-check that we're being called in the correct thread.
assert fExecutor.isInExecutorThread();
@ -110,7 +110,7 @@ implements ITerminate
// canceled if shutdownServices() is called before the sequence
// completes.
fInitializationSequence = new PDAServicesInitSequence(
getSession(), program, requestPort, eventPort,
getSession(), this, program,
new RequestMonitor(ImmediateExecutor.getInstance(), rm) {
@Override
protected void handleCompleted() {

View file

@ -12,24 +12,18 @@
*******************************************************************************/
package org.eclipse.dd.examples.pda.launch;
import java.io.File;
import java.io.IOException;
import java.net.ServerSocket;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.dd.dsf.concurrent.DataRequestMonitor;
import org.eclipse.dd.dsf.concurrent.IDsfStatusConstants;
import org.eclipse.dd.dsf.concurrent.Query;
import org.eclipse.dd.dsf.service.DsfServicesTracker;
import org.eclipse.dd.examples.pda.PDAPlugin;
import org.eclipse.dd.examples.pda.service.PDABackend;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunch;
@ -92,79 +86,22 @@ public class PDALaunchDelegate extends LaunchConfigurationDelegate {
abort("Perl program unspecified.", null);
}
int requestPort = findFreePort();
int eventPort = findFreePort();
if (requestPort == -1 || eventPort == -1) {
abort("Unable to find free port", null);
}
launchProcess(launch, program, requestPort, eventPort);
PDALaunch pdaLaunch = (PDALaunch)launch;
initServices(pdaLaunch, program, requestPort, eventPort);
initServices(pdaLaunch, program);
createProcess(pdaLaunch);
}
/**
* Launches PDA interpreter with the given program.
*
* @param launch Launch that will contain the new process.
* @param program PDA program to use in the interpreter.
* @param requestPort The port number for connecting the request socket.
* @param eventPort The port number for connecting the events socket.
*
* @throws CoreException
*/
private void launchProcess(ILaunch launch, String program, int requestPort, int eventPort) throws CoreException {
List<String> commandList = new ArrayList<String>();
// Get Java VM path
String javaVMHome = System.getProperty("java.home");
String javaVMExec = javaVMHome + File.separatorChar + "bin" + File.separatorChar + "java";
if (File.separatorChar == '\\') {
javaVMExec += ".exe";
}
File exe = new File(javaVMExec);
if (!exe.exists()) {
abort(MessageFormat.format("Specified java VM executable {0} does not exist.", new Object[]{javaVMExec}), null);
}
commandList.add(javaVMExec);
commandList.add("-cp");
commandList.add(File.pathSeparator + PDAPlugin.getFileInPlugin(new Path("bin")));
commandList.add("org.eclipse.dd.examples.pdavm.PDAVirtualMachine");
// Add PDA program
IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(program));
if (!file.exists()) {
abort(MessageFormat.format("PDA program {0} does not exist.", new Object[] {file.getFullPath().toString()}), null);
}
commandList.add(file.getLocation().toOSString());
// Add debug arguments - i.e. '-debug requestPort eventPort'
commandList.add("-debug");
commandList.add("" + requestPort);
commandList.add("" + eventPort);
// Launch the perl process.
String[] commandLine = commandList.toArray(new String[commandList.size()]);
Process process = DebugPlugin.exec(commandLine, null);
// Create a debug platform process object and add it to the launch.
DebugPlugin.newProcess(launch, process, javaVMHome);
}
/**
* Calls the launch to initialize DSF services for this launch.
*/
private void initServices(final PDALaunch pdaLaunch, final String program, final int requestPort, final int eventPort)
private void initServices(final PDALaunch pdaLaunch, final String program)
throws CoreException
{
// Synchronization object to use when waiting for the services initialization.
Query<Object> initQuery = new Query<Object>() {
@Override
protected void execute(DataRequestMonitor<Object> rm) {
pdaLaunch.initializeServices(program, requestPort, eventPort, rm);
pdaLaunch.initializeServices(program, rm);
}
};
@ -180,6 +117,36 @@ public class PDALaunchDelegate extends LaunchConfigurationDelegate {
}
}
private void createProcess(final PDALaunch pdaLaunch) throws CoreException {
// Synchronization object to use when waiting for the services initialization.
Query<Object[]> initQuery = new Query<Object[]>() {
@Override
protected void execute(DataRequestMonitor<Object[]> rm) {
DsfServicesTracker tracker = new DsfServicesTracker(PDAPlugin.getBundleContext(), pdaLaunch.getSession().getId());
PDABackend backend = tracker.getService(PDABackend.class);
if (backend == null) {
PDAPlugin.failRequest(rm, IDsfStatusConstants.INVALID_STATE, "PDA Backend service not available");
return;
}
Object[] retVal = new Object[] { backend.getProcess(), backend.getProcessName() };
rm.setData(retVal);
rm.done();
}
};
// Submit the query to the executor.
pdaLaunch.getSession().getExecutor().execute(initQuery);
try {
// Block waiting for query results.
Object[] processData = initQuery.get();
DebugPlugin.newProcess(pdaLaunch, (Process)processData[0], (String)processData[1]);
} catch (InterruptedException e1) {
throw new DebugException(new Status(IStatus.ERROR, PDAPlugin.PLUGIN_ID, DebugException.INTERNAL_ERROR, "Interrupted Exception in dispatch thread", e1)); //$NON-NLS-1$
} catch (ExecutionException e1) {
throw new DebugException(new Status(IStatus.ERROR, PDAPlugin.PLUGIN_ID, DebugException.REQUEST_FAILED, "Error in launch sequence", e1.getCause())); //$NON-NLS-1$
}
}
/**
* Throws an exception with a new status containing the given
* message and optional exception.
@ -191,24 +158,4 @@ public class PDALaunchDelegate extends LaunchConfigurationDelegate {
private void abort(String message, Throwable e) throws CoreException {
throw new CoreException(new Status(IStatus.ERROR, PDAPlugin.PLUGIN_ID, 0, message, e));
}
/**
* Returns a free port number on localhost, or -1 if unable to find a free port.
*/
public static int findFreePort() {
ServerSocket socket= null;
try {
socket= new ServerSocket(0);
return socket.getLocalPort();
} catch (IOException e) {
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
}
}
}
return -1;
}
}

View file

@ -14,6 +14,7 @@ import org.eclipse.dd.dsf.concurrent.RequestMonitor;
import org.eclipse.dd.dsf.concurrent.Sequence;
import org.eclipse.dd.dsf.debug.service.BreakpointsMediator;
import org.eclipse.dd.dsf.service.DsfSession;
import org.eclipse.dd.examples.pda.service.PDABackend;
import org.eclipse.dd.examples.pda.service.PDABreakpointAttributeTranslator;
import org.eclipse.dd.examples.pda.service.PDABreakpoints;
import org.eclipse.dd.examples.pda.service.PDACommandControl;
@ -40,8 +41,16 @@ public class PDAServicesInitSequence extends Sequence {
{
@Override
public void execute(RequestMonitor requestMonitor) {
// Create the connection to PDA debugger.
fCommandControl = new PDACommandControl(fSession, fProgram, fRequestPort, fEventPort);
// Start PDA back end debugger service.
new PDABackend(fSession, fLaunch, fProgram).initialize(requestMonitor);
}
},
new Step()
{
@Override
public void execute(RequestMonitor requestMonitor) {
// Start PDA command control service.
fCommandControl = new PDACommandControl(fSession);
fCommandControl.initialize(requestMonitor);
}
},
@ -70,7 +79,7 @@ public class PDAServicesInitSequence extends Sequence {
bpmService.initialize(new RequestMonitor(getExecutor(), requestMonitor) {
@Override
protected void handleSuccess() {
bpmService.startTrackingBreakpoints(fCommandControl.getVirtualMachineDMContext(), requestMonitor);
bpmService.startTrackingBreakpoints(fCommandControl.getContext(), requestMonitor);
}
});
}
@ -99,28 +108,26 @@ public class PDAServicesInitSequence extends Sequence {
new Step() {
@Override
public void execute(RequestMonitor requestMonitor) {
fRunControl.resume(fCommandControl.getVirtualMachineDMContext(), requestMonitor);
fRunControl.resume(fCommandControl.getContext(), requestMonitor);
}
},
};
// Sequence input parameters, used in initializing services.
private PDALaunch fLaunch;
private DsfSession fSession;
private String fProgram;
private int fRequestPort;
private int fEventPort;
// Service references, initialized when created and used in initializing other services.
private PDACommandControl fCommandControl;
private PDARunControl fRunControl;
public PDAServicesInitSequence(DsfSession session, String program, int requestPort, int eventPort, RequestMonitor rm)
public PDAServicesInitSequence(DsfSession session, PDALaunch launch, String program, RequestMonitor rm)
{
super(session.getExecutor(), rm);
fLaunch = launch;
fSession = session;
fProgram = program;
fRequestPort = requestPort;
fEventPort = eventPort;
}
@Override

View file

@ -0,0 +1,320 @@
/*******************************************************************************
* Copyright (c) 2008 Nokia Corporation.
* All rights reserved. This fProgram 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:
* Ling Wang (Nokia) - initial version. Sep 28, 2008
*******************************************************************************/
package org.eclipse.dd.examples.pda.service;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.dd.dsf.concurrent.RequestMonitor;
import org.eclipse.dd.dsf.concurrent.Sequence;
import org.eclipse.dd.dsf.concurrent.ThreadSafe;
import org.eclipse.dd.dsf.concurrent.Sequence.Step;
import org.eclipse.dd.dsf.service.AbstractDsfService;
import org.eclipse.dd.dsf.service.DsfSession;
import org.eclipse.dd.examples.pda.PDAPlugin;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.Launch;
import org.osgi.framework.BundleContext;
/**
* Service that manages the backend process: starting the process
* and monitoring for its shutdown.
*/
public class PDABackend extends AbstractDsfService {
private int fRequestPort;
private int fEventPort;
@ThreadSafe
private OutputStream fRequestOutputStream;
@ThreadSafe
private InputStream fRequestInputStream;
@ThreadSafe
private InputStream fEventInputStream;
private String fProgram;
private Process fBackendProcess;
private String fBackendProcessName;
/**
*
* @param session
* @param launch - can be null, e.g. for JUnit test.
* @param program - can be a full path or a workspace resource path, must denote an existing file.
*/
public PDABackend(DsfSession session, Launch launch, String program) {
super(session);
fProgram = program;
}
@Override
protected BundleContext getBundleContext() {
return PDAPlugin.getBundleContext();
}
public Process getProcess() {
return fBackendProcess;
}
public String getProcessName() {
return fBackendProcessName;
}
public String getPorgramName() {
return fProgram;
}
@Override
public void initialize(final RequestMonitor rm) {
super.initialize(new RequestMonitor(getExecutor(), rm) {
@Override
protected void handleSuccess() {
doInitialize(rm);
}
});
}
private void doInitialize(final RequestMonitor requestMonitor) {
final Sequence.Step[] initializeSteps = new Sequence.Step[] {
new Step() {
// Launch the back end debugger process.
@Override
public void execute(final RequestMonitor rm) {
new Job("Start PDA Virtual Machine") {
@Override
protected IStatus run(IProgressMonitor monitor) {
try {
fBackendProcess = launchPDABackendDebugger();
} catch (CoreException e) {
rm.setStatus(e.getStatus());
}
rm.done();
return Status.OK_STATUS;
}
}.schedule();
}
@Override
public void rollBack(RequestMonitor rm) {
if (fBackendProcess != null)
fBackendProcess.destroy();
rm.done();
}
},
new Step() {
@Override
public void execute(final RequestMonitor rm) {
// To avoid blocking the DSF dispatch thread use a job to initialize communication sockets.
new Job("PDA Socket Initialize") {
@Override
protected IStatus run(IProgressMonitor monitor) {
try {
// give interpreter a chance to start
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
Socket socket = new Socket("localhost", fRequestPort);
fRequestOutputStream = socket.getOutputStream();
fRequestInputStream = socket.getInputStream();
// give interpreter a chance to open next socket
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
socket = new Socket("localhost", fEventPort);
fEventInputStream = socket.getInputStream();
} catch (UnknownHostException e) {
rm.setStatus(new Status(
IStatus.ERROR, PDAPlugin.PLUGIN_ID, REQUEST_FAILED, "Unable to connect to PDA VM", e));
} catch (IOException e) {
rm.setStatus(new Status(
IStatus.ERROR, PDAPlugin.PLUGIN_ID, REQUEST_FAILED, "Unable to connect to PDA VM", e));
}
rm.done();
return Status.OK_STATUS;
}
}.schedule();
}
},
new Step() { // register the service
@Override
public void execute(RequestMonitor rm) {
// Register this service
register(new String[] { PDABackend.class.getName() },
new Hashtable<String, String>());
rm.done();
}
},
};
Sequence startupSequence = new Sequence(getExecutor(), requestMonitor) {
@Override public Step[] getSteps() { return initializeSteps; }
};
getExecutor().execute(startupSequence);
}
/**
* Returns a free port number on localhost, or -1 if unable to find a free port.
*/
public static int findFreePort() {
ServerSocket socket= null;
try {
socket= new ServerSocket(0);
return socket.getLocalPort();
} catch (IOException e) {
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
}
}
}
return -1;
}
private void abort(String message, Throwable e) throws CoreException {
throw new CoreException(new Status(IStatus.ERROR, PDAPlugin.PLUGIN_ID, 0, message, e));
}
private Process launchPDABackendDebugger() throws CoreException {
List<String> commandList = new ArrayList<String>();
// Get Java VM path
String javaVMHome = System.getProperty("java.home");
String javaVMExec = javaVMHome + File.separatorChar + "bin" + File.separatorChar + "java";
if (File.separatorChar == '\\') {
javaVMExec += ".exe";
}
File exe = new File(javaVMExec);
if (!exe.exists()) {
abort(MessageFormat.format("Specified java VM executable {0} does not exist.", new Object[]{javaVMExec}), null);
}
fBackendProcessName = javaVMExec;
commandList.add(javaVMExec);
commandList.add("-cp");
commandList.add(File.pathSeparator + PDAPlugin.getFileInPlugin(new Path("bin")));
commandList.add("org.eclipse.dd.examples.pdavm.PDAVirtualMachine");
String absolutePath = fProgram;
// check if fProgram is already a full path of an existing file
// Note if "fProgram" is workspace resource path like /ProjectName/file.pda, we should not
// change it to absolute path, otherwise the breakpoints in the PDA file won't work.
// See PDABreakpoints.doInsertBreakpoint() for more.
File f = new File(fProgram);
if (! f.exists()) {
// Try to locate it in workspace
IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(fProgram));
if (file.exists())
absolutePath = file.getLocation().toPortableString();
else
abort(MessageFormat.format("PDA program {0} does not exist.", new Object[] {file.getFullPath().toPortableString()}), null);
}
commandList.add(absolutePath);
fRequestPort = findFreePort();
fEventPort = findFreePort();
if (fRequestPort == -1 || fEventPort == -1) {
abort("Unable to find free port", null);
}
// Add debug arguments - i.e. '-debug fRequestPort fEventPort'
commandList.add("-debug");
commandList.add("" + fRequestPort);
commandList.add("" + fEventPort);
// Launch the perl process.
String[] commandLine = commandList.toArray(new String[commandList.size()]);
PDAPlugin.debug("Start PDA Virtual Machine:\n" + commandList);
Process process = DebugPlugin.exec(commandLine, null);
return process;
}
@Override
public void shutdown(final RequestMonitor rm) {
fBackendProcess.destroy();
try {
if (fRequestInputStream != null)
fRequestInputStream.close();
if (fRequestOutputStream != null)
fRequestOutputStream.close();
if (fEventInputStream != null)
fEventInputStream.close();
} catch (IOException e) {
// ignore
}
unregister();
rm.done();
}
/*
* =========== Following are PDA debugger specific ====================
*
* Caller should make sure these are called after the PDABackend is initialized.
*/
public OutputStream getRequestOutputStream() {
return fRequestOutputStream;
}
public InputStream getRequestInputStream() {
return fRequestInputStream;
}
public InputStream getEventInputStream() {
return fEventInputStream;
}
}

View file

@ -164,7 +164,7 @@ public class PDABreakpoints extends AbstractDsfService implements IBreakpoints
public void getBreakpoints(final IBreakpointsTargetDMContext context, final DataRequestMonitor<IBreakpointDMContext[]> rm) {
// Validate the context
if (!fCommandControl.getVirtualMachineDMContext().equals(context)) {
if (!fCommandControl.getContext().equals(context)) {
PDAPlugin.failRequest(rm, INVALID_HANDLE, "Invalid breakpoints target context");
return;
}
@ -230,7 +230,7 @@ public class PDABreakpoints extends AbstractDsfService implements IBreakpoints
// installed already. PDA can only track a single breakpoint at a
// given line, attempting to set the second breakpoint should fail.
final BreakpointDMContext breakpointCtx =
new BreakpointDMContext(getSession().getId(), fCommandControl.getVirtualMachineDMContext(), line);
new BreakpointDMContext(getSession().getId(), fCommandControl.getContext(), line);
if (fBreakpoints.contains(breakpointCtx)) {
PDAPlugin.failRequest(rm, REQUEST_FAILED, "Breakpoint already set");
return;
@ -244,7 +244,7 @@ public class PDABreakpoints extends AbstractDsfService implements IBreakpoints
// still being processed here.
fBreakpoints.add(breakpointCtx);
fCommandControl.queueCommand(
new PDASetBreakpointCommand(fCommandControl.getVirtualMachineDMContext(), line, false),
new PDASetBreakpointCommand(fCommandControl.getContext(), line, false),
new DataRequestMonitor<PDACommandResult>(getExecutor(), rm) {
@Override
protected void handleSuccess() {
@ -286,7 +286,7 @@ public class PDABreakpoints extends AbstractDsfService implements IBreakpoints
// installed already. PDA can only track a single watchpoint for a given
// function::variable, attempting to set the second breakpoint should fail.
final WatchpointDMContext watchpointCtx =
new WatchpointDMContext(getSession().getId(), fCommandControl.getVirtualMachineDMContext(), function, variable);
new WatchpointDMContext(getSession().getId(), fCommandControl.getContext(), function, variable);
if (fBreakpoints.contains(watchpointCtx)) {
PDAPlugin.failRequest(rm, REQUEST_FAILED, "Watchpoint already set");
return;
@ -310,7 +310,7 @@ public class PDABreakpoints extends AbstractDsfService implements IBreakpoints
// still being processed here.
fBreakpoints.add(watchpointCtx);
fCommandControl.queueCommand(
new PDAWatchCommand(fCommandControl.getVirtualMachineDMContext(), function, variable, watchOperation),
new PDAWatchCommand(fCommandControl.getContext(), function, variable, watchOperation),
new DataRequestMonitor<PDACommandResult>(getExecutor(), rm) {
@Override
protected void handleSuccess() {
@ -350,7 +350,7 @@ public class PDABreakpoints extends AbstractDsfService implements IBreakpoints
fBreakpoints.remove(bpCtx);
fCommandControl.queueCommand(
new PDAClearBreakpointCommand(fCommandControl.getVirtualMachineDMContext(), bpCtx.fLine),
new PDAClearBreakpointCommand(fCommandControl.getContext(), bpCtx.fLine),
new DataRequestMonitor<PDACommandResult>(getExecutor(), rm));
}
@ -360,7 +360,7 @@ public class PDABreakpoints extends AbstractDsfService implements IBreakpoints
// Watchpoints are cleared using the same command, but with a "no watch" operation
fCommandControl.queueCommand(
new PDAWatchCommand(
fCommandControl.getVirtualMachineDMContext(), bpCtx.fFunction, bpCtx.fVariable, PDAWatchCommand.WatchOperation.NONE),
fCommandControl.getContext(), bpCtx.fFunction, bpCtx.fVariable, PDAWatchCommand.WatchOperation.NONE),
new DataRequestMonitor<PDACommandResult>(getExecutor(), rm));
}

View file

@ -14,8 +14,6 @@ import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
@ -36,6 +34,7 @@ import org.eclipse.dd.dsf.concurrent.RequestMonitor;
import org.eclipse.dd.dsf.concurrent.ThreadSafe;
import org.eclipse.dd.dsf.debug.service.command.ICommand;
import org.eclipse.dd.dsf.debug.service.command.ICommandControl;
import org.eclipse.dd.dsf.debug.service.command.ICommandControlService;
import org.eclipse.dd.dsf.debug.service.command.ICommandListener;
import org.eclipse.dd.dsf.debug.service.command.ICommandResult;
import org.eclipse.dd.dsf.debug.service.command.ICommandToken;
@ -52,7 +51,7 @@ import org.osgi.framework.BundleContext;
/**
* Service that handles communication with a PDA debugger back end.
*/
public class PDACommandControl extends AbstractDsfService implements ICommandControl {
public class PDACommandControl extends AbstractDsfService implements ICommandControlService {
// Structure used to store command information in services internal queues.
private static class CommandHandle {
@ -67,11 +66,8 @@ public class PDACommandControl extends AbstractDsfService implements ICommandCon
}
}
// Parameters that the command control is created with.
final private String fProgram;
final private int fRequestPort;
final private int fEventPort;
private PDABackend fBackend;
// Queue of commands waiting to be sent to the debugger. As long as commands
// are in this queue, they can still be removed by clients.
private final List<CommandHandle> fCommandQueue = new LinkedList<CommandHandle>();
@ -98,14 +94,10 @@ public class PDACommandControl extends AbstractDsfService implements ICommandCon
// Sockets for communicating with PDA debugger
@ThreadSafe
private Socket fRequestSocket;
@ThreadSafe
private PrintWriter fRequestWriter;
@ThreadSafe
private BufferedReader fRequestReader;
@ThreadSafe
private Socket fEventSocket;
@ThreadSafe
private BufferedReader fEventReader;
// Jobs servicing the sockets.
@ -115,14 +107,9 @@ public class PDACommandControl extends AbstractDsfService implements ICommandCon
/**
* Command control constructor.
* @param session The DSF session that this service is a part of.
* @param requestPort Port number for sending PDA commands.
* @param eventPort Port for listening to PDA events.
*/
public PDACommandControl(DsfSession session, String program, int requestPort, int eventPort) {
public PDACommandControl(DsfSession session) {
super(session);
fProgram = program;
fRequestPort = requestPort;
fEventPort = eventPort;
}
@Override
@ -137,8 +124,10 @@ public class PDACommandControl extends AbstractDsfService implements ICommandCon
}
private void doInitialize(final RequestMonitor rm) {
fBackend = getServicesTracker().getService(PDABackend.class);
// Create the control's data model context.
fDMContext = new PDAVirtualMachineDMContext(getSession().getId(), fProgram);
fDMContext = new PDAVirtualMachineDMContext(getSession().getId(), fBackend.getPorgramName());
// Add a listener for PDA events to track the started/terminated state.
addEventListener(new IEventListener() {
@ -151,60 +140,25 @@ public class PDACommandControl extends AbstractDsfService implements ICommandCon
}
});
// Request monitor that will be invoked when the socket initialization is
// completed.
final RequestMonitor socketsInitializeRm = new RequestMonitor(getExecutor(), rm) {
@Override
protected void handleSuccess() {
// Register the service with OSGi as the last step in initialization of
// the service.
register(
new String[]{ ICommandControl.class.getName(), PDACommandControl.class.getName() },
new Hashtable<String,String>());
rm.done();
}
};
// Get intput/output streams from the backend service.
//
fRequestWriter = new PrintWriter(fBackend.getRequestOutputStream());
fRequestReader = new BufferedReader(new InputStreamReader(fBackend.getRequestInputStream()));
fEventReader = new BufferedReader(new InputStreamReader(fBackend.getEventInputStream()));
fEventDispatchJob = new EventDispatchJob();
fEventDispatchJob.schedule();
// To avoid blocking the DSF dispatch thread use a job to initialize communication sockets.
new Job("PDA Initialize") {
@Override
protected IStatus run(IProgressMonitor monitor) {
try {
// give interpreter a chance to start
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
fRequestSocket = new Socket("localhost", fRequestPort);
fRequestWriter = new PrintWriter(fRequestSocket.getOutputStream());
fRequestReader = new BufferedReader(new InputStreamReader(fRequestSocket.getInputStream()));
// give interpreter a chance to open next socket
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
fEventSocket = new Socket("localhost", fEventPort);
fEventReader = new BufferedReader(new InputStreamReader(fEventSocket.getInputStream()));
fCommandSendJob = new CommandSendJob();
fCommandSendJob.schedule();
fEventDispatchJob = new EventDispatchJob();
fEventDispatchJob.schedule();
fCommandSendJob = new CommandSendJob();
fCommandSendJob.schedule();
// Register the service with OSGi as the last step in initialization of
// the service.
register(
new String[]{ ICommandControl.class.getName(), PDACommandControl.class.getName() },
new Hashtable<String,String>());
socketsInitializeRm.done();
} catch (UnknownHostException e) {
socketsInitializeRm.setStatus(new Status(
IStatus.ERROR, PDAPlugin.PLUGIN_ID, REQUEST_FAILED, "Unable to connect to PDA VM", e));
socketsInitializeRm.done();
} catch (IOException e) {
socketsInitializeRm.setStatus(new Status(
IStatus.ERROR, PDAPlugin.PLUGIN_ID, REQUEST_FAILED, "Unable to connect to PDA VM", e));
socketsInitializeRm.done();
}
return Status.OK_STATUS;
}
}.schedule();
rm.done();
}
@Override
@ -499,10 +453,14 @@ public class PDACommandControl extends AbstractDsfService implements ICommandCon
* @see PDAVirtualMachineDMContext
*/
@ThreadSafe
public PDAVirtualMachineDMContext getVirtualMachineDMContext() {
public PDAVirtualMachineDMContext getContext() {
return fDMContext;
}
public String getId() {
return fBackend.getPorgramName();
}
private void setStarted() {
// Mark the command control as started and ready to process commands.
fStarted = true;
@ -511,16 +469,17 @@ public class PDACommandControl extends AbstractDsfService implements ICommandCon
processQueues();
// Issue a data model event.
getSession().dispatchEvent(new PDAStartedEvent(getVirtualMachineDMContext()), getProperties());
getSession().dispatchEvent(new PDAStartedEvent(getContext()), getProperties());
}
/**
* Returns whether the PDA debugger has started and is processing commands.
*/
public boolean isStarted() {
return fStarted;
public boolean isActive() {
return fStarted && !isTerminated();
}
@ThreadSafe
private synchronized void setTerminated() {
// Set terminated may be called more than once: by event listener thread,
@ -533,7 +492,7 @@ public class PDACommandControl extends AbstractDsfService implements ICommandCon
processQueues();
// Issue a data model event.
getSession().dispatchEvent(new PDATerminatedEvent(getVirtualMachineDMContext()), getProperties());
getSession().dispatchEvent(new PDATerminatedEvent(getContext()), getProperties());
}
}

View file

@ -344,6 +344,12 @@ public class PDAExpressions extends AbstractDsfService implements ICachingServic
rm.done();
}
}
@Override
protected void handleErrorOrWarning() {
rm.setData(new String[] { NATURAL_FORMAT, STRING_FORMAT });
rm.done();
}
});
}

View file

@ -270,7 +270,7 @@ public class PDARegisters extends AbstractDsfService
// only needs to be reset upon the "registers" event and is available
// all the time.
fNamesCache = new CommandCache(getSession(), fCommandControl);
fNamesCache.setContextAvailable(fCommandControl.getVirtualMachineDMContext(), true);
fNamesCache.setContextAvailable(fCommandControl.getContext(), true);
// Add the register service as a listener to PDA events, to catch
// the "registers" events from the command control.

View file

@ -257,7 +257,7 @@ public class PDARunControl extends AbstractDsfService
private void doInitialize(final RequestMonitor rm) {
// Cache a reference to the command control and the virtual machine context
fCommandControl = getServicesTracker().getService(PDACommandControl.class);
fDMContext = fCommandControl.getVirtualMachineDMContext();
fDMContext = fCommandControl.getContext();
// Create the main thread context.
fThreads.put(

View file

@ -15,6 +15,7 @@ import org.eclipse.dd.dsf.datamodel.AbstractDMContext;
import org.eclipse.dd.dsf.datamodel.IDMContext;
import org.eclipse.dd.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext;
import org.eclipse.dd.dsf.debug.service.IRunControl.IContainerDMContext;
import org.eclipse.dd.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext;
import org.eclipse.dd.dsf.service.DsfSession;
/**
@ -38,7 +39,7 @@ import org.eclipse.dd.dsf.service.DsfSession;
* </p>
*/
public class PDAVirtualMachineDMContext extends PlatformObject
implements IContainerDMContext, IBreakpointsTargetDMContext
implements ICommandControlDMContext, IContainerDMContext, IBreakpointsTargetDMContext
{
final static IDMContext[] EMPTY_PARENTS_ARRAY = new IDMContext[0];
@ -67,6 +68,10 @@ public class PDAVirtualMachineDMContext extends PlatformObject
return "pda[" + getSessionId() + "]";
}
public String getCommandControlId() {
return getProgram();
}
/**
* @see AbstractDMContext#getAdapter(Class)
*/

View file

@ -82,7 +82,7 @@ public class BasicTests extends CommandControlTestsBase {
}
});
final PDATestCommand testCommand = new PDATestCommand(fCommandControl.getVirtualMachineDMContext(), "data 1");
final PDATestCommand testCommand = new PDATestCommand(fCommandControl.getContext(), "data 1");
// Test sending the command and checking all listeners were called.
Query<PDACommandResult> sendCommandQuery = new Query<PDACommandResult>() {

View file

@ -26,6 +26,7 @@ import org.eclipse.dd.dsf.concurrent.DsfExecutor;
import org.eclipse.dd.dsf.concurrent.Query;
import org.eclipse.dd.dsf.debug.service.command.IEventListener;
import org.eclipse.dd.dsf.service.DsfSession;
import org.eclipse.dd.examples.pda.service.PDABackend;
import org.eclipse.dd.examples.pda.service.PDACommandControl;
import org.eclipse.dd.examples.pda.service.commands.PDACommandResult;
import org.eclipse.dd.tests.pda.util.Launching;
@ -41,7 +42,7 @@ public class CommandControlTestsBase {
protected DsfExecutor fExecutor;
protected DsfSession fSession;
protected Process fPDAProcess;
protected PDABackend fPDABackend;
protected PDACommandControl fCommandControl;
private BlockingQueue<Object> fEventsQueue = new LinkedBlockingQueue<Object>();
@ -57,16 +58,20 @@ public class CommandControlTestsBase {
}
};
int requestPort = Launching.findFreePort();
int eventPort = Launching.findFreePort();
fPDAProcess = Launching.launchPDA(fProgram, requestPort, eventPort);
fOutputReader = new BufferedReader(new InputStreamReader(fPDAProcess.getInputStream()));
Assert.assertEquals("-debug " + requestPort + " " + eventPort, fOutputReader.readLine());
fExecutor = new DefaultDsfExecutor();
fSession = DsfSession.startSession(fExecutor, "PDA Test");
fCommandControl = new PDACommandControl(fSession, fProgram, requestPort, eventPort);
Process proc = Launching.launchPDA(fSession, null, fProgram);
Assert.assertNotNull(proc);
// Remember the backend service of this session.
// Note this must be called after the above LaunchPDA().
fPDABackend = Launching.getBackendService();
fOutputReader = new BufferedReader(new InputStreamReader(proc.getInputStream()));
Assert.assertTrue(fOutputReader.readLine().contains("-debug"));
fCommandControl = new PDACommandControl(fSession);
fCommandControl.addEventListener(new IEventListener() {
public void eventReceived(Object output) {
@ -82,8 +87,9 @@ public class CommandControlTestsBase {
@After
public void shutdown() throws CoreException, InterruptedException, ExecutionException, IOException {
fOutputReader.close();
fPDAProcess.destroy();
if (fOutputReader != null) {
fOutputReader.close();
}
class ShutdownCommandServiceQuery extends Query<Object> {
@Override
@ -97,6 +103,19 @@ public class CommandControlTestsBase {
fExecutor.execute(shutdownQuery);
shutdownQuery.get();
}
class ShutdownBackendServiceQuery extends Query<Object> {
@Override
protected void execute(DataRequestMonitor<Object> rm) {
fPDABackend.shutdown(rm);
}
};
if (fExecutor != null) {
ShutdownBackendServiceQuery shutdownQuery = new ShutdownBackendServiceQuery();
fExecutor.execute(shutdownQuery);
shutdownQuery.get();
}
}
protected void sendCommand(String command) throws Throwable {
@ -105,7 +124,7 @@ public class CommandControlTestsBase {
protected void sendCommand(String command, String expectedResult) throws Throwable {
final PDATestCommand testCommand = new PDATestCommand(fCommandControl.getVirtualMachineDMContext(), command);
final PDATestCommand testCommand = new PDATestCommand(fCommandControl.getContext(), command);
// Test sending the command and checking all listeners were called.
Query<PDACommandResult> sendCommandQuery = new Query<PDACommandResult>() {

View file

@ -7,85 +7,52 @@
*
* Contributors:
* Wind River Systems - initial API and implementation
* Nokia - create and use backend service.
*******************************************************************************/
package org.eclipse.dd.tests.pda.util;
import java.io.File;
import java.io.IOException;
import java.net.ServerSocket;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import junit.framework.Assert;
import java.util.concurrent.ExecutionException;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.dd.examples.pda.PDAPlugin;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.dd.dsf.concurrent.DataRequestMonitor;
import org.eclipse.dd.dsf.concurrent.Query;
import org.eclipse.dd.dsf.service.DsfSession;
import org.eclipse.dd.examples.pda.service.PDABackend;
import org.eclipse.debug.core.Launch;
/**
*
*/
public class Launching {
public static Process launchPDA(String pdaProgram, int requestPort, int eventPort) throws CoreException {
Assert.assertTrue("Invalid request port", requestPort > 0);
Assert.assertTrue("Invalid event port", eventPort > 0);
List<String> commandList = new ArrayList<String>();
// Get Java VM path
String javaVMHome = System.getProperty("java.home");
String javaVMExec = javaVMHome + File.separatorChar + "bin" + File.separatorChar + "java";
File exe = new File(javaVMExec);
if (!exe.exists()) {
throw new CoreException(new Status(
IStatus.ERROR, PDAPlugin.PLUGIN_ID, 0,
MessageFormat.format("Specified java VM executable {0} does not exist.", new Object[]{javaVMExec}), null));
}
commandList.add(javaVMExec);
commandList.add("-cp");
commandList.add(File.pathSeparator + PDAPlugin.getFileInPlugin(new Path("bin")));
commandList.add("org.eclipse.dd.examples.pdavm.PDAVirtualMachine");
commandList.add(pdaProgram);
private static PDABackend fBackendService;
public static Process launchPDA(DsfSession session, Launch launch, String pdaProgram) throws CoreException {
// if in debug mode, add debug arguments - i.e. '-debug requestPort eventPort'
commandList.add("-debug");
commandList.add("" + requestPort);
commandList.add("" + eventPort);
String[] commandLine = commandList.toArray(new String[commandList.size()]);
return DebugPlugin.exec(commandLine, null);
}
/**
* Returns a free port number on localhost, or -1 if unable to find a free port.
*
* @return a free port number on localhost, or -1 if unable to find a free port
*/
public static int findFreePort() {
ServerSocket socket= null;
try {
socket= new ServerSocket(0);
return socket.getLocalPort();
} catch (IOException e) {
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
}
class InitializeBackendServiceQuery extends Query<Object> {
@Override
protected void execute(DataRequestMonitor<Object> rm) {
fBackendService.initialize(rm);
}
}
return -1;
}
};
fBackendService = new PDABackend(session, launch, pdaProgram);
InitializeBackendServiceQuery initQuery = new InitializeBackendServiceQuery();
session.getExecutor().execute(initQuery);
try {
initQuery.get();
} catch (InterruptedException e) {
e.printStackTrace();
return null;
} catch (ExecutionException e) {
e.printStackTrace();
return null;
}
return fBackendService.getProcess();
}
public static PDABackend getBackendService() {
return fBackendService;
}
}

View file

@ -7,6 +7,7 @@
*
* Contributors:
* Wind River Systems - initial API and implementation
* Nokia - create and use backend service.
*******************************************************************************/
package org.eclipse.dd.gdb.internal.ui.actions;
@ -19,8 +20,10 @@ import org.eclipse.dd.dsf.debug.ui.actions.DsfCommandRunnable;
import org.eclipse.dd.dsf.service.DsfServicesTracker;
import org.eclipse.dd.dsf.service.DsfSession;
import org.eclipse.dd.dsf.ui.viewmodel.datamodel.IDMVMContext;
import org.eclipse.dd.gdb.internal.provisional.service.IGDBBackend;
import org.eclipse.dd.gdb.internal.provisional.service.command.IGDBControl;
import org.eclipse.dd.gdb.internal.ui.GdbUIPlugin;
import org.eclipse.dd.mi.service.IMIBackend;
import org.eclipse.debug.core.commands.IDebugCommandRequest;
import org.eclipse.debug.core.commands.IEnabledStateRequest;
import org.eclipse.debug.core.commands.ITerminateHandler;
@ -62,14 +65,14 @@ public class DsfTerminateCommand implements ITerminateHandler {
new DsfRunnable() {
public void run() {
// Get the processes service and the exec context.
IGDBControl gdbControl = fTracker.getService(IGDBControl.class);
if (gdbControl == null || dmc == null) {
IGDBBackend gdbBackend = fTracker.getService(IGDBBackend.class);
if (gdbBackend == null || dmc == null) {
// Context or service already invalid.
request.setEnabled(false);
request.done();
} else {
// Check the teriminate.
request.setEnabled(!gdbControl.isGDBExited());
// Check the terminate.
request.setEnabled(gdbBackend.getState() == IMIBackend.State.STARTED);
request.done();
}
}

View file

@ -7,27 +7,18 @@
*
* Contributors:
* Ericsson - initial API and implementation
* Nokia - create and use backend service.
*******************************************************************************/
package org.eclipse.dd.gdb.internal.provisional.launching;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
import org.eclipse.cdt.debug.internal.core.sourcelookup.CSourceLookupDirector;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.variables.VariablesPlugin;
import org.eclipse.dd.dsf.concurrent.DataRequestMonitor;
import org.eclipse.dd.dsf.concurrent.DsfExecutor;
import org.eclipse.dd.dsf.concurrent.RequestMonitor;
@ -39,6 +30,7 @@ import org.eclipse.dd.dsf.service.DsfServicesTracker;
import org.eclipse.dd.gdb.internal.GdbPlugin;
import org.eclipse.dd.gdb.internal.provisional.IGDBLaunchConfigurationConstants;
import org.eclipse.dd.gdb.internal.provisional.actions.IConnect;
import org.eclipse.dd.gdb.internal.provisional.service.IGDBBackend;
import org.eclipse.dd.gdb.internal.provisional.service.SessionType;
import org.eclipse.dd.gdb.internal.provisional.service.command.IGDBControl;
import org.eclipse.dd.mi.service.CSourceLookup;
@ -59,9 +51,6 @@ import org.eclipse.dd.mi.service.command.output.MIInfo;
public class FinalLaunchSequence extends Sequence {
Step[] fSteps = new Step[] {
/*
* Fetch the control service for later use
*/
new Step() { @Override
public void execute(RequestMonitor requestMonitor) {
fTracker = new DsfServicesTracker(GdbPlugin.getBundleContext(), fLaunch.getSession().getId());
@ -73,6 +62,22 @@ public class FinalLaunchSequence extends Sequence {
fTracker = null;
requestMonitor.done();
}},
/*
* Fetch the GDBBackend service for later use
*/
new Step() { @Override
public void execute(RequestMonitor requestMonitor) {
fGDBBackend = fTracker.getService(IGDBBackend.class);
if (fGDBBackend == null) {
requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot obtain GDBBackend service", null)); //$NON-NLS-1$
}
requestMonitor.done();
}},
/*
* Fetch the control service for later use
*/
new Step() { @Override
public void execute(RequestMonitor requestMonitor) {
fCommandControl = fTracker.getService(IGDBControl.class);
@ -89,8 +94,8 @@ public class FinalLaunchSequence extends Sequence {
new Step() { @Override
public void execute(final RequestMonitor requestMonitor) {
try {
final String gdbinitFile = fLaunch.getLaunchConfiguration().getAttribute(IGDBLaunchConfigurationConstants.ATTR_GDB_INIT,
IGDBLaunchConfigurationConstants.DEBUGGER_GDB_INIT_DEFAULT );
final String gdbinitFile = fGDBBackend.getGDBInitFile();
if (gdbinitFile != null && gdbinitFile.length() > 0) {
fCommandControl.queueCommand(
new CLISource(fCommandControl.getContext(), gdbinitFile),
@ -134,7 +139,7 @@ public class FinalLaunchSequence extends Sequence {
if (!noFileCommand && execPath != null && !execPath.isEmpty()) {
fCommandControl.queueCommand(
new MIFileExecAndSymbols(fCommandControl.getContext(),
execPath.toOSString()),
execPath.toPortableString()),
new DataRequestMonitor<MIInfo>(getExecutor(), requestMonitor));
} else {
requestMonitor.done();
@ -146,11 +151,9 @@ public class FinalLaunchSequence extends Sequence {
new Step() { @Override
public void execute(final RequestMonitor requestMonitor) {
try {
String args = fLaunch.getLaunchConfiguration().getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS,
(String)null);
String args = fGDBBackend.getProgramArguments();
if (args != null) {
args = VariablesPlugin.getDefault().getStringVariableManager().performStringSubstitution(args);
fCommandControl.queueCommand(
new MIGDBSetArgs(fCommandControl.getContext(), args),
new DataRequestMonitor<MIInfo>(getExecutor(), requestMonitor));
@ -167,58 +170,24 @@ public class FinalLaunchSequence extends Sequence {
*/
new Step() {
private File getWorkingDirectory(RequestMonitor requestMonitor) {
private IPath getWorkingDirectory(RequestMonitor requestMonitor) {
IPath path = null;
try {
String location = fLaunch.getLaunchConfiguration().getAttribute(ICDTLaunchConfigurationConstants.ATTR_WORKING_DIRECTORY,
(String)null);
if (location != null) {
String expandedLocation = VariablesPlugin.getDefault().getStringVariableManager().performStringSubstitution(location);
if (expandedLocation.length() > 0) {
path = new Path(expandedLocation);
}
}
if (path == null) {
// default working dir is the project if this config has a project
ICProject cp = LaunchUtils.getCProject(fLaunch.getLaunchConfiguration());
if (cp != null) {
IProject p = cp.getProject();
return p.getLocation().toFile();
}
} else {
if (path.isAbsolute()) {
File dir = new File(path.toOSString());
if (dir.isDirectory()) {
return dir;
}
} else {
IResource res = ResourcesPlugin.getWorkspace().getRoot().findMember(path);
if (res instanceof IContainer && res.exists()) {
return res.getLocation().toFile();
}
}
requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1,
LaunchMessages.getString("AbstractCLaunchDelegate.Working_directory_does_not_exist"), //$NON-NLS-1$
new FileNotFoundException(LaunchMessages.getFormattedString(
"AbstractCLaunchDelegate.WORKINGDIRECTORY_PATH_not_found", path.toOSString())))); //$NON-NLS-1$
requestMonitor.done();
}
path = fGDBBackend.getGDBWorkingDirectory();
} catch (CoreException e) {
requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot get working directory", e)); //$NON-NLS-1$
requestMonitor.done();
}
return null;
return path;
}
@Override
public void execute(final RequestMonitor requestMonitor) {
File dir = getWorkingDirectory(requestMonitor);
IPath dir = getWorkingDirectory(requestMonitor);
if (dir != null) {
fCommandControl.queueCommand(
new MIEnvironmentCD(fCommandControl.getContext(), dir.getAbsolutePath()),
new MIEnvironmentCD(fCommandControl.getContext(), dir.toPortableString()),
new DataRequestMonitor<MIInfo>(getExecutor(), requestMonitor));
} else {
requestMonitor.done();
@ -314,9 +283,8 @@ public class FinalLaunchSequence extends Sequence {
new Step() { @Override
public void execute(final RequestMonitor requestMonitor) {
try {
@SuppressWarnings("unchecked")
List<String> p = fLaunch.getLaunchConfiguration().getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_SOLIB_PATH,
new ArrayList<String>(1));
List<String> p = fGDBBackend.getSharedLibraryPaths();
if (p.size() > 0) {
String[] paths = p.toArray(new String[p.size()]);
fCommandControl.queueCommand(
@ -508,8 +476,10 @@ public class FinalLaunchSequence extends Sequence {
SessionType fSessionType;
boolean fAttach;
IGDBControl fCommandControl;
IMIProcesses fProcService;
private IGDBControl fCommandControl;
private IGDBBackend fGDBBackend;
private IMIProcesses fProcService;
DsfServicesTracker fTracker;
public FinalLaunchSequence(DsfExecutor executor, GdbLaunch launch, SessionType sessionType, boolean attach) {

View file

@ -323,7 +323,8 @@ public class GdbLaunchDelegate extends LaunchConfigurationDelegate
return false;
}
private IDsfDebugServicesFactory newServiceFactory(String version) {
// A subclass can override this method and provide its own ServiceFactory.
protected IDsfDebugServicesFactory newServiceFactory(String version) {
if (isNonStopSession && isNonStopSupported(version)) {
return new GdbDebugServicesFactoryNS(version);

View file

@ -7,6 +7,7 @@
*
* Contributors:
* Wind River Systems - initial API and implementation
* Nokia - created GDBBackend service. Sep. 2008
*******************************************************************************/
package org.eclipse.dd.gdb.internal.provisional.launching;
@ -27,12 +28,21 @@ import org.eclipse.dd.dsf.debug.service.ISourceLookup.ISourceLookupDMContext;
import org.eclipse.dd.dsf.debug.service.command.ICommandControlService;
import org.eclipse.dd.dsf.service.DsfSession;
import org.eclipse.dd.mi.service.CSourceLookup;
import org.eclipse.dd.mi.service.IMIBackend;
import org.eclipse.dd.mi.service.IMIProcesses;
import org.eclipse.dd.mi.service.MIBreakpointsManager;
public class ServicesLaunchSequence extends Sequence {
Step[] fSteps = new Step[] {
new Step() {
@Override
public void execute(RequestMonitor requestMonitor) {
// Create the back end GDB service.
//
fLaunch.getServiceFactory().createService(IMIBackend.class, fSession, fLaunch.getLaunchConfiguration()).initialize(requestMonitor);
}
},
// Create and initialize the Connection service.
new Step() {
@Override

View file

@ -30,6 +30,7 @@ import org.eclipse.dd.dsf.debug.service.command.ICommandControl;
import org.eclipse.dd.dsf.service.DsfServicesTracker;
import org.eclipse.dd.dsf.service.IDsfService;
import org.eclipse.dd.gdb.internal.GdbPlugin;
import org.eclipse.dd.mi.service.IMIBackend;
import org.eclipse.dd.mi.service.MIBreakpointsManager;
public class ShutdownSequence extends Sequence {
@ -126,6 +127,11 @@ public class ShutdownSequence extends Sequence {
public void execute(RequestMonitor requestMonitor) {
shutdownService(ICommandControl.class, requestMonitor);
}
}, new Step() {
@Override
public void execute(RequestMonitor requestMonitor) {
shutdownService(IMIBackend.class, requestMonitor);
}
}, new Step() {
@Override
public void execute(RequestMonitor requestMonitor) {

View file

@ -0,0 +1,556 @@
/*******************************************************************************
* Copyright (c) 2006, 2008 Wind River Systems, Nokia 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:
* Ling Wang (Nokia) - initial API and implementation with some code moved from GDBControl.
* Wind River System
* Ericsson
*******************************************************************************/
package org.eclipse.dd.gdb.internal.provisional.service;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
import org.eclipse.cdt.utils.spawner.ProcessFactory;
import org.eclipse.cdt.utils.spawner.Spawner;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.variables.VariablesPlugin;
import org.eclipse.dd.dsf.concurrent.DsfRunnable;
import org.eclipse.dd.dsf.concurrent.IDsfStatusConstants;
import org.eclipse.dd.dsf.concurrent.RequestMonitor;
import org.eclipse.dd.dsf.concurrent.Sequence;
import org.eclipse.dd.dsf.service.AbstractDsfService;
import org.eclipse.dd.dsf.service.DsfSession;
import org.eclipse.dd.gdb.internal.GdbPlugin;
import org.eclipse.dd.gdb.internal.provisional.IGDBLaunchConfigurationConstants;
import org.eclipse.dd.gdb.internal.provisional.launching.LaunchUtils;
import org.eclipse.dd.gdb.internal.provisional.service.command.GDBControl.InitializationShutdownStep;
import org.eclipse.dd.mi.service.IMIBackend;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.osgi.framework.BundleContext;
/**
* Implementation of {@link IGDBBackend} for the common case where GDB is launched
* in local file system on host PC where Eclipse runs. This also manages some GDB parameters
* from a given launch configuration.<br>
* <br>
* You can subclass for you special needs.
*
* @since 1.1
*/
public class GDBBackend extends AbstractDsfService implements IGDBBackend {
private ILaunchConfiguration fLaunchConfiguration;
/**
* Command line to start GDB.
*/
private List<String> fGDBCommandLine;
/*
* Parameters for launching GDB.
*/
private IPath fProgramPath;
private IPath fGDBWorkingDirectory;
private String fGDBInitFile;
private List<String> fSharedLibPaths;
private String fProgramArguments;
/**
* Unique ID of this service instance.
*/
private final String fBackendId;
private static int fgInstanceCounter = 0;
/*
* Service state parameters.
*/
private MonitorJob fMonitorJob;
private Process fProcess;
private int fGDBExitValue;
private int fGDBLaunchTimeout = 30;
public GDBBackend(DsfSession session, ILaunchConfiguration lc) {
super(session);
fBackendId = "gdb[" +Integer.toString(fgInstanceCounter++) + "]"; //$NON-NLS-1$//$NON-NLS-2$
fLaunchConfiguration = lc;
try {
ICProject cproject = LaunchUtils.verifyCProject(lc);
fProgramPath = LaunchUtils.verifyProgramPath(lc, cproject);
} catch (CoreException e) {
fProgramPath = new Path(""); //$NON-NLS-1$
}
}
@Override
public void initialize(final RequestMonitor requestMonitor) {
super.initialize( new RequestMonitor(getExecutor(), requestMonitor) {
@Override
protected void handleSuccess() {
doInitialize(requestMonitor);
}
});
}
public void doInitialize(final RequestMonitor requestMonitor) {
final Sequence.Step[] initializeSteps = new Sequence.Step[] {
new GDBProcessStep(InitializationShutdownStep.Direction.INITIALIZING),
new MonitorJobStep(InitializationShutdownStep.Direction.INITIALIZING),
new RegisterStep(InitializationShutdownStep.Direction.INITIALIZING),
};
Sequence startupSequence = new Sequence(getExecutor(), requestMonitor) {
@Override public Step[] getSteps() { return initializeSteps; }
};
getExecutor().execute(startupSequence);
}
@Override
public void shutdown(final RequestMonitor requestMonitor) {
final Sequence.Step[] shutdownSteps = new Sequence.Step[] {
new RegisterStep(InitializationShutdownStep.Direction.SHUTTING_DOWN),
new MonitorJobStep(InitializationShutdownStep.Direction.SHUTTING_DOWN),
new GDBProcessStep(InitializationShutdownStep.Direction.SHUTTING_DOWN),
};
Sequence shutdownSequence = new Sequence(getExecutor(), requestMonitor) {
@Override public Step[] getSteps() { return shutdownSteps; }
};
getExecutor().execute(shutdownSequence);
}
private IPath getGDBPath() {
IPath retVal = new Path(IGDBLaunchConfigurationConstants.DEBUGGER_DEBUG_NAME_DEFAULT);
try {
retVal = new Path(fLaunchConfiguration.getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUG_NAME,
IGDBLaunchConfigurationConstants.DEBUGGER_DEBUG_NAME_DEFAULT));
} catch (CoreException e) {
}
return retVal;
}
public List<String> getGDBCommandLine() {
if (fGDBCommandLine == null)
{
fGDBCommandLine = new ArrayList<String>();
// The goal here is to keep options to an absolute minimum.
// All configuration should be done in the launch sequence
// to allow for easy overriding.
fGDBCommandLine.add(getGDBPath().toOSString());
fGDBCommandLine.add("--interpreter"); //$NON-NLS-1$
fGDBCommandLine.add("mi"); //$NON-NLS-1$
// Don't read the gdbinit file here. It is read explicitly in
// the LaunchSequence to make it easier to customize.
fGDBCommandLine.add("--nx"); //$NON-NLS-1$
}
return fGDBCommandLine;
}
public String getGDBInitFile() throws CoreException {
if (fGDBInitFile == null) {
fGDBInitFile = fLaunchConfiguration.getAttribute(IGDBLaunchConfigurationConstants.ATTR_GDB_INIT, ""); //$NON-NLS-1$
}
return fGDBInitFile;
}
public IPath getGDBWorkingDirectory() throws CoreException {
if (fGDBWorkingDirectory == null) {
// First try to use the user-sepcified working directory for the debugged program.
// This is fine only with local debug.
// For remote debug, the working dir of the debugged program will be on remote device
// and hence not applicable. In such case we may just use debugged program path on host
// as the working dir for GDB.
// However, we cannot find a standard/common way to distinguish remote debug from local
// debug. For instance, a local debug may also use gdbserver+gdb. So it's up to each
// debugger implementation to make the distinction.
//
IPath path = null;
String location = fLaunchConfiguration.getAttribute(ICDTLaunchConfigurationConstants.ATTR_WORKING_DIRECTORY, (String)null);
if (location != null) {
String expandedLocation = VariablesPlugin.getDefault().getStringVariableManager().performStringSubstitution(location);
if (expandedLocation.length() > 0) {
path = new Path(expandedLocation);
}
}
if (path != null) {
// Some validity check. Should have been done by UI code.
if (path.isAbsolute()) {
File dir = new File(path.toPortableString());
if (! dir.isDirectory())
path = null;
} else {
IResource res = ResourcesPlugin.getWorkspace().getRoot().findMember(path);
if (res instanceof IContainer && res.exists()) {
path = res.getLocation();
}
else
// Relative but not found in workspace.
path = null;
}
}
if (path == null) {
// default working dir is the project if this config has a project
ICProject cp = LaunchUtils.getCProject(fLaunchConfiguration);
if (cp != null) {
IProject p = cp.getProject();
path = p.getLocation();
}
else {
// no meaningful value found. Just return null.
}
}
fGDBWorkingDirectory = path;
}
return fGDBWorkingDirectory;
}
public String getProgramArguments() throws CoreException {
if (fProgramArguments == null) {
fProgramArguments = fLaunchConfiguration.getAttribute(
ICDTLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS,
(String)null);
if (fProgramArguments != null)
fProgramArguments = VariablesPlugin.getDefault().getStringVariableManager().performStringSubstitution(fProgramArguments);
}
return fProgramArguments;
}
public IPath getProgramPath() {
return fProgramPath;
}
@SuppressWarnings("unchecked")
public List<String> getSharedLibraryPaths() throws CoreException {
if (fSharedLibPaths == null) {
fSharedLibPaths = fLaunchConfiguration.getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_SOLIB_PATH,
new ArrayList<String>(0));
}
return fSharedLibPaths;
}
/*
* Launch GDB process.
* Allow subclass to override.
*/
protected Process launchGDBProcess() throws CoreException {
List<String> gdbCmdLine = getGDBCommandLine();
String[] commandLine = gdbCmdLine.toArray(new String[gdbCmdLine.size()]);
Process proc = null;
try {
proc = ProcessFactory.getFactory().exec(commandLine);
} catch (IOException e) {
String message = "Error while launching command " + gdbCmdLine.toString(); //$NON-NLS-1$
throw new CoreException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, message, e));
}
return proc;
}
public Process getProcess() {
return fProcess;
}
public OutputStream getMIOutputStream() {
return fProcess.getOutputStream();
};
public InputStream getMIInputStream() {
return fProcess.getInputStream();
};
public String getId() {
return fBackendId;
}
public void interrupt() {
if (fProcess instanceof Spawner) {
Spawner gdbSpawner = (Spawner) fProcess;
gdbSpawner.interrupt();
}
}
public void destroy() {
if (getState() != State.STARTED)
return;
// destroy() should be supported even if it's not spawner.
fProcess.destroy();
/*
if (fProcess instanceof Spawner) {
Spawner gdbSpawner = (Spawner) fProcess;
gdbSpawner.destroy();
}
*/
}
public State getState() {
if (fMonitorJob == null) {
return State.NOT_INITIALIZED;
} else if (fMonitorJob.fExited) {
return State.TERMINATED;
} else {
return State.STARTED;
}
}
public int getExitCode() {
return fGDBExitValue;
}
@Override
protected BundleContext getBundleContext() {
return GdbPlugin.getBundleContext();
}
protected class GDBProcessStep extends InitializationShutdownStep {
GDBProcessStep(Direction direction) { super(direction); }
@Override
public void initialize(final RequestMonitor requestMonitor) {
class GDBLaunchMonitor {
boolean fLaunched = false;
boolean fTimedOut = false;
}
final GDBLaunchMonitor fGDBLaunchMonitor = new GDBLaunchMonitor();
final RequestMonitor gdbLaunchRequestMonitor = new RequestMonitor(getExecutor(), null) {
@Override
protected void handleCompleted() {
if (!fGDBLaunchMonitor.fTimedOut) {
fGDBLaunchMonitor.fLaunched = true;
if (!isSuccess()) {
requestMonitor.setStatus(getStatus());
}
requestMonitor.done();
}
}
};
final Job startGdbJob = new Job("Start GDB Process Job") { //$NON-NLS-1$
@Override
protected IStatus run(IProgressMonitor monitor) {
List<String> commandList = getGDBCommandLine();
try {
fProcess = launchGDBProcess();
} catch(CoreException e) {
String message = "Error while launching command " + commandList.toString(); //$NON-NLS-1$
gdbLaunchRequestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, message, e));
gdbLaunchRequestMonitor.done();
return Status.OK_STATUS;
}
try {
InputStream stream = fProcess.getInputStream();
Reader r = new InputStreamReader(stream);
BufferedReader reader = new BufferedReader(r);
String line;
while ((line = reader.readLine()) != null) {
line = line.trim();
//System.out.println("GDB " + line);
if (line.endsWith("(gdb)")) { //$NON-NLS-1$
break;
}
}
} catch (IOException e) {
gdbLaunchRequestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Error reading GDB STDOUT", e)); //$NON-NLS-1$
gdbLaunchRequestMonitor.done();
return Status.OK_STATUS;
}
gdbLaunchRequestMonitor.done();
return Status.OK_STATUS;
}
};
startGdbJob.schedule();
getExecutor().schedule(new Runnable() {
public void run() {
// Only process the event if we have not finished yet (hit the breakpoint).
if (!fGDBLaunchMonitor.fLaunched) {
fGDBLaunchMonitor.fTimedOut = true;
Thread jobThread = startGdbJob.getThread();
if (jobThread != null) {
jobThread.interrupt();
}
requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, DebugException.TARGET_REQUEST_FAILED, "Timed out trying to launch GDB.", null)); //$NON-NLS-1$
requestMonitor.done();
}
}},
fGDBLaunchTimeout, TimeUnit.SECONDS);
}
@Override
protected void shutdown(final RequestMonitor requestMonitor) {
new Job("Terminating GDB process.") { //$NON-NLS-1$
@Override
protected IStatus run(IProgressMonitor monitor) {
destroy();
int attempts = 0;
while (attempts < 10) {
try {
// Don't know if we really need the exit value... but what the hell.
fGDBExitValue = fProcess.exitValue(); // throws exception if process not exited
requestMonitor.done();
return Status.OK_STATUS;
} catch (IllegalThreadStateException ie) {
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
attempts++;
}
requestMonitor.setStatus(new Status(
IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "Process terminate failed", null)); //$NON-NLS-1$
requestMonitor.done();
return Status.OK_STATUS;
}
}.schedule();
}
}
protected class MonitorJobStep extends InitializationShutdownStep {
MonitorJobStep(Direction direction) { super(direction); }
@Override
public void initialize(final RequestMonitor requestMonitor) {
fMonitorJob = new MonitorJob(
fProcess,
new DsfRunnable() {
public void run() {
requestMonitor.done();
}
});
fMonitorJob.schedule();
}
@Override
protected void shutdown(RequestMonitor requestMonitor) {
if (!fMonitorJob.fExited) {
fMonitorJob.kill();
}
requestMonitor.done();
}
}
protected class RegisterStep extends InitializationShutdownStep {
RegisterStep(Direction direction) { super(direction); }
@Override
public void initialize(final RequestMonitor requestMonitor) {
register(
new String[]{ IMIBackend.class.getName(),
IGDBBackend.class.getName() },
new Hashtable<String,String>());
/*
* This event is not consumed by any one at present, instead it's
* the GDBControlInitializedDMEvent that's used to indicate that GDB
* back end is ready for MI commands. But we still fire the event as
* it does no harm and may be needed sometime.... 09/29/08
*/
getSession().dispatchEvent(
new BackendStateChangedEvent(getSession().getId(), getId(), IMIBackend.State.STARTED),
getProperties());
requestMonitor.done();
}
@Override
protected void shutdown(RequestMonitor requestMonitor) {
unregister();
requestMonitor.done();
}
}
/**
* Monitors a system process, waiting for it to terminate, and
* then notifies the associated runtime process.
*/
private class MonitorJob extends Job {
boolean fExited = false;
DsfRunnable fMonitorStarted;
Process fMonProcess;
@Override
protected IStatus run(IProgressMonitor monitor) {
synchronized(fMonProcess) {
getExecutor().submit(fMonitorStarted);
while (!fExited) {
try {
fMonProcess.waitFor();
fGDBExitValue = fMonProcess.exitValue();
} catch (InterruptedException ie) {
// clear interrupted state
Thread.interrupted();
} finally {
fExited = true;
getSession().dispatchEvent(
new BackendStateChangedEvent(getSession().getId(), getId(), IMIBackend.State.TERMINATED),
getProperties());
}
}
}
return Status.OK_STATUS;
}
MonitorJob(Process process, DsfRunnable monitorStarted) {
super("GDB process monitor job."); //$NON-NLS-1$
fMonProcess = process;
fMonitorStarted = monitorStarted;
setSystem(true);
}
void kill() {
synchronized(fMonProcess) {
if (!fExited) {
getThread().interrupt();
}
}
}
}
}

View file

@ -8,6 +8,7 @@
* Contributors:
* Wind River Systems - initial API and implementation
* Ericsson AB - Modified for additional functionality
* Nokia - create and use backend service.
*******************************************************************************/
package org.eclipse.dd.gdb.internal.provisional.service;
@ -29,7 +30,6 @@ import org.eclipse.dd.dsf.debug.service.IProcesses.IProcessDMContext;
import org.eclipse.dd.dsf.debug.service.IProcesses.IThreadDMContext;
import org.eclipse.dd.dsf.service.DsfSession;
import org.eclipse.dd.gdb.internal.GdbPlugin;
import org.eclipse.dd.gdb.internal.provisional.service.command.IGDBControl;
import org.eclipse.dd.mi.service.IMIExecutionDMContext;
import org.eclipse.dd.mi.service.IMIProcesses;
import org.eclipse.dd.mi.service.MIRunControl;
@ -37,7 +37,7 @@ import org.eclipse.dd.mi.service.command.events.MIEvent;
import org.eclipse.dd.mi.service.command.events.MIThreadExitEvent;
public class GDBRunControl extends MIRunControl {
private IGDBControl fGdb;
private IGDBBackend fGdb;
private IMIProcesses fProcService;
// Record list of execution contexts
@ -60,7 +60,7 @@ public class GDBRunControl extends MIRunControl {
private void doInitialize(final RequestMonitor requestMonitor) {
fGdb = getServicesTracker().getService(IGDBControl.class);
fGdb = getServicesTracker().getService(IGDBBackend.class);
fProcService = getServicesTracker().getService(IMIProcesses.class);
register(new String[]{IRunControl.class.getName(),

View file

@ -9,6 +9,7 @@
* Wind River Systems - initial API and implementation
* Ericsson - Modified for additional functionality
* Ericsson - Version 7.0
* Nokia - create and use backend service.
*******************************************************************************/
package org.eclipse.dd.gdb.internal.provisional.service;
@ -27,13 +28,12 @@ import org.eclipse.dd.dsf.debug.service.IProcesses.IProcessDMContext;
import org.eclipse.dd.dsf.debug.service.IProcesses.IThreadDMContext;
import org.eclipse.dd.dsf.service.DsfSession;
import org.eclipse.dd.gdb.internal.GdbPlugin;
import org.eclipse.dd.gdb.internal.provisional.service.command.IGDBControl;
import org.eclipse.dd.mi.service.IMIExecutionDMContext;
import org.eclipse.dd.mi.service.IMIProcesses;
import org.eclipse.dd.mi.service.MIRunControl;
public class GDBRunControl_7_0 extends MIRunControl {
private IGDBControl fGdb;
private IGDBBackend fGdb;
private IMIProcesses fProcService;
public GDBRunControl_7_0(DsfSession session) {
@ -52,7 +52,7 @@ public class GDBRunControl_7_0 extends MIRunControl {
private void doInitialize(final RequestMonitor requestMonitor) {
fGdb = getServicesTracker().getService(IGDBControl.class);
fGdb = getServicesTracker().getService(IGDBBackend.class);
fProcService = getServicesTracker().getService(IMIProcesses.class);
register(new String[]{IRunControl.class.getName(), MIRunControl.class.getName()},

View file

@ -7,6 +7,7 @@
*
* Contributors:
* Ericsson - initial API and implementation
* Nokia - create and use backend service.
*******************************************************************************/
package org.eclipse.dd.gdb.internal.provisional.service;
@ -28,6 +29,7 @@ import org.eclipse.dd.gdb.internal.provisional.service.command.GDBControl;
import org.eclipse.dd.gdb.internal.provisional.service.command.GDBControl_7_0;
import org.eclipse.dd.mi.service.CSourceLookup;
import org.eclipse.dd.mi.service.ExpressionService;
import org.eclipse.dd.mi.service.IMIBackend;
import org.eclipse.dd.mi.service.MIBreakpoints;
import org.eclipse.dd.mi.service.MIBreakpointsManager;
import org.eclipse.dd.mi.service.MIDisassembly;
@ -52,13 +54,21 @@ public class GdbDebugServicesFactory extends AbstractDsfDebugServicesFactory {
public <V> V createService(Class<V> clazz, DsfSession session, Object ... optionalArguments) {
if (MIBreakpointsManager.class.isAssignableFrom(clazz)) {
return (V)createBreakpointManagerService(session);
} else if (ICommandControl.class.isAssignableFrom(clazz)) {
}
else if (ICommandControl.class.isAssignableFrom(clazz)) {
for (Object arg : optionalArguments) {
if (arg instanceof ILaunchConfiguration) {
return (V)createCommandControl(session, (ILaunchConfiguration)arg);
}
}
}
else if (IMIBackend.class.isAssignableFrom(clazz)) {
for (Object arg : optionalArguments) {
if (arg instanceof ILaunchConfiguration) {
return (V)createBackendGDBService(session, (ILaunchConfiguration)arg);
}
}
}
return super.createService(clazz, session);
@ -80,6 +90,10 @@ public class GdbDebugServicesFactory extends AbstractDsfDebugServicesFactory {
return new GDBControl(session, config);
}
protected IMIBackend createBackendGDBService(DsfSession session, ILaunchConfiguration lc) {
return new GDBBackend(session, lc);
}
@Override
protected IDisassembly createDisassemblyService(DsfSession session) {
return new MIDisassembly(session);

View file

@ -0,0 +1,96 @@
/*******************************************************************************
* Copyright (c) 2008 Nokia Corporation.
* 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:
* Ling Wang (Nokia) - initial version. Sep, 2008
*******************************************************************************/
package org.eclipse.dd.gdb.internal.provisional.service;
import java.util.List;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.dd.mi.service.IMIBackend;
/**
* Service that manages back end GDB process, such as launching and monitoring
* GDB process, managing certain GDB parameters/options. This service makes it
* easy for debugger implementations to customize the way to start GDB process
* and convert some parameters if needed. See bug 240092 for more.<br>
* <br>
* A base implementation {@link GDBBackend} is provided that should be
* sufficient for most cases. But if you have special needs, it's recommended to
* subclass the base implementation. <br>
* <br>
* Here are some special cases: <br>
* Example #1: GDB is usually launched on the host machine where Eclipse is
* running, but it can also be launched on a remote machine through, say, SSH. <br>
* Example #2: GDB is usually launched in the host file system, but it can also
* be launched in a chroot'ed file system such as Scratchbox (see
* http://www.scratchbox.org)<br>
*
* @since 1.1
*/
public interface IGDBBackend extends IMIBackend {
/**
* Return the command line that is used to launch GDB. <br>
* Note it's not needed to put debugged binary in the command, as that will
* be set by a MI command.
*
* @return
*/
public List<String> getGDBCommandLine();
/**
* Get path of the debugged program on host.
*
* @return IPath
*/
public IPath getProgramPath();
/**
* Get init file for GDB.
*
* @return file name, may have relative or absolute path, or empty string
* ("") indicating an init file is not specified.
* @throws CoreException
* - error in getting the option.
*/
public String getGDBInitFile() throws CoreException;
/**
* get arguments for the debugged program.
*
* @return String
* @throws CoreException
* - error in getting the option.
*/
public String getProgramArguments() throws CoreException;
/**
* Get working directory for GDB.
*
* @return IPath - null if no meaningful value found.
* @throws CoreException
* - if any error occurs.
*/
public IPath getGDBWorkingDirectory() throws CoreException;
/**
* @throws CoreException
* - error in getting the option.
*/
public List<String> getSharedLibraryPaths() throws CoreException;
/**
* Sends an interrupt signal to the GDB process.
*/
public void interrupt();
}

View file

@ -1,102 +0,0 @@
/*******************************************************************************
* Copyright (c) 2007, 2008 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Wind River Systems - initial API and implementation
*******************************************************************************/
package org.eclipse.dd.gdb.internal.provisional.service.command;
import java.io.IOException;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.RejectedExecutionException;
import org.eclipse.dd.dsf.concurrent.DsfRunnable;
import org.eclipse.dd.dsf.debug.service.command.ICommandControlService;
import org.eclipse.dd.dsf.service.DsfSession;
import org.eclipse.dd.mi.service.command.AbstractCLIProcess;
/**
*
*/
class GDBCLIProcess extends AbstractCLIProcess {
public GDBCLIProcess(ICommandControlService commandControl) throws IOException {
super(commandControl);
}
/**
* @see java.lang.Process#waitFor()
*/
@Override
public int waitFor() throws InterruptedException {
if (!DsfSession.isSessionActive(getSession().getId())) return 0;
Process process = null;
try {
process = getSession().getExecutor().submit(new Callable<Process>() {
public Process call() throws Exception {
if (isDisposed()) return null;
return ((IGDBControl)getCommandControlService()).getGDBProcess();
}}).get();
} catch (RejectedExecutionException e) {
} catch (ExecutionException e) {
}
if (process == null) return 0;
return process.waitFor();
}
/**
* @see java.lang.Process#exitValue()
*/
@Override
public int exitValue() {
if (!DsfSession.isSessionActive(getSession().getId())) return 0;
try {
return getSession().getExecutor().submit(new Callable<Integer>() {
public Integer call() throws Exception {
if (!DsfSession.isSessionActive(getSession().getId())) {
return new Integer(-1);
} else {
if (isDisposed()) return new Integer(-1);
IGDBControl gdb = (IGDBControl)getCommandControlService();
if (!gdb.isGDBExited()) {
throw new IllegalThreadStateException("GDB Process has not exited"); //$NON-NLS-1$
}
return gdb.getGDBExitCode();
}
}}).get().intValue();
} catch (RejectedExecutionException e) {
} catch (InterruptedException e) {
} catch (ExecutionException e) {
if (e.getCause() instanceof RuntimeException) {
throw (RuntimeException)e.getCause();
}
}
return 0;
}
/**
* @see java.lang.Process#destroy()
*/
@Override
public void destroy() {
try {
getSession().getExecutor().execute(new DsfRunnable() { public void run() {
if (!DsfSession.isSessionActive(getSession().getId())) return;
if (isDisposed()) return;
IGDBControl gdb = (IGDBControl)getCommandControlService();
gdb.destroy();
}});
} catch (RejectedExecutionException e) {
// Session disposed.
}
}
}

View file

@ -8,31 +8,21 @@
* Contributors:
* Wind River Systems - initial API and implementation
* Ericsson - Modified for additional features in DSF Reference implementation
* Nokia - create and use backend service.
*******************************************************************************/
package org.eclipse.dd.gdb.internal.provisional.service.command;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
import org.eclipse.cdt.utils.pty.PTY;
import org.eclipse.cdt.utils.spawner.ProcessFactory;
import org.eclipse.cdt.utils.spawner.Spawner;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.dd.dsf.concurrent.DataRequestMonitor;
import org.eclipse.dd.dsf.concurrent.DsfRunnable;
import org.eclipse.dd.dsf.concurrent.IDsfStatusConstants;
@ -49,13 +39,17 @@ import org.eclipse.dd.dsf.service.DsfSession;
import org.eclipse.dd.gdb.internal.GdbPlugin;
import org.eclipse.dd.gdb.internal.provisional.launching.GdbLaunch;
import org.eclipse.dd.gdb.internal.provisional.launching.LaunchUtils;
import org.eclipse.dd.gdb.internal.provisional.service.IGDBBackend;
import org.eclipse.dd.gdb.internal.provisional.service.SessionType;
import org.eclipse.dd.mi.service.IMIBackend;
import org.eclipse.dd.mi.service.IMIProcesses;
import org.eclipse.dd.mi.service.MIProcesses;
import org.eclipse.dd.mi.service.IMIBackend.BackendStateChangedEvent;
import org.eclipse.dd.mi.service.MIProcesses.ContainerStartedDMEvent;
import org.eclipse.dd.mi.service.command.AbstractCLIProcess;
import org.eclipse.dd.mi.service.command.AbstractMIControl;
import org.eclipse.dd.mi.service.command.CLIEventProcessor;
import org.eclipse.dd.mi.service.command.MIBackendCLIProcess;
import org.eclipse.dd.mi.service.command.MIControlDMContext;
import org.eclipse.dd.mi.service.command.MIInferiorProcess;
import org.eclipse.dd.mi.service.command.MIRunControlEventProcessor;
@ -74,7 +68,7 @@ import org.osgi.framework.BundleContext;
/**
* GDB Debugger control implementation. This implementation extends the
* base MI control implementation to provide the GDB-specific debugger
* features. This includes:<br * - Launching and monitoring the GDB process,<br>
* features. This includes:<br>
* - CLI console support,<br>
* - inferior process status tracking.<br>
*/
@ -92,7 +86,7 @@ public class GDBControl extends AbstractMIControl implements IGDBControl {
}
/**
* Event indicating that the back end process has terminated.
* Event indicating that the CommandControl (back end process) has terminated.
*/
private static class GDBControlShutdownDMEvent extends AbstractDMEvent<ICommandControlDMContext>
implements ICommandControlShutdownDMEvent
@ -102,22 +96,16 @@ public class GDBControl extends AbstractMIControl implements IGDBControl {
}
}
private static int fgInstanceCounter = 0;
private final GDBControlDMContext fControlDmc;
private GDBControlDMContext fControlDmc;
private SessionType fSessionType;
private boolean fAttach;
private IGDBBackend fMIBackend;
private boolean fConnected = true;
private MonitorJob fMonitorJob;
private IPath fGdbPath;
private IPath fExecPath;
private Process fProcess;
private int fGDBExitValue;
private int fGDBLaunchTimeout = 30;
private MIRunControlEventProcessor fMIEventProcessor;
private CLIEventProcessor fCLICommandProcessor;
private AbstractCLIProcess fCLIProcess;
@ -126,16 +114,9 @@ public class GDBControl extends AbstractMIControl implements IGDBControl {
private PTY fPty;
public GDBControl(DsfSession session, ILaunchConfiguration config) {
super(session, "gdbcontrol[" + ++fgInstanceCounter + "]", false); //$NON-NLS-1$ //$NON-NLS-2$
super(session, false);
fSessionType = LaunchUtils.getSessionType(config);
fAttach = LaunchUtils.getIsAttach(config);
fGdbPath = LaunchUtils.getGDBPath(config);
try {
fExecPath = LaunchUtils.verifyProgramPath(config, LaunchUtils.getCProject(config));
} catch (CoreException e) {
fExecPath = new Path(""); //$NON-NLS-1$
}
fControlDmc = new GDBControlDMContext(session.getId(), getId());
}
@Override
@ -154,9 +135,14 @@ public class GDBControl extends AbstractMIControl implements IGDBControl {
}
public void doInitialize(final RequestMonitor requestMonitor) {
fMIBackend = getServicesTracker().getService(IGDBBackend.class);
// getId uses the MIBackend service, which is why we must wait until we
// have it, before we can create this context.
fControlDmc = new GDBControlDMContext(getSession().getId(), getId());
final Sequence.Step[] initializeSteps = new Sequence.Step[] {
new GDBProcessStep(InitializationShutdownStep.Direction.INITIALIZING),
new MonitorJobStep(InitializationShutdownStep.Direction.INITIALIZING),
new CommandMonitoringStep(InitializationShutdownStep.Direction.INITIALIZING),
new InferiorInputOutputInitStep(InitializationShutdownStep.Direction.INITIALIZING),
new CommandProcessorsStep(InitializationShutdownStep.Direction.INITIALIZING),
@ -176,8 +162,6 @@ public class GDBControl extends AbstractMIControl implements IGDBControl {
new CommandProcessorsStep(InitializationShutdownStep.Direction.SHUTTING_DOWN),
new InferiorInputOutputInitStep(InitializationShutdownStep.Direction.SHUTTING_DOWN),
new CommandMonitoringStep(InitializationShutdownStep.Direction.SHUTTING_DOWN),
new MonitorJobStep(InitializationShutdownStep.Direction.SHUTTING_DOWN),
new GDBProcessStep(InitializationShutdownStep.Direction.SHUTTING_DOWN),
};
Sequence shutdownSequence = new Sequence(getExecutor(), requestMonitor) {
@Override public Step[] getSteps() { return shutdownSteps; }
@ -186,6 +170,10 @@ public class GDBControl extends AbstractMIControl implements IGDBControl {
}
public String getId() {
return fMIBackend.getId();
}
@Override
public MIControlDMContext getControlDMContext() {
return fControlDmc;
@ -202,23 +190,6 @@ public class GDBControl extends AbstractMIControl implements IGDBControl {
public boolean getIsAttachSession() {
return fAttach;
}
public boolean canInterrupt() {
return fProcess instanceof Spawner;
}
public void interrupt() {
if (fProcess instanceof Spawner) {
Spawner gdbSpawner = (Spawner) fProcess;
gdbSpawner.interrupt();
}
}
public void destroy() {
if (fProcess instanceof Spawner) {
Spawner gdbSpawner = (Spawner) fProcess;
gdbSpawner.destroy();
}
}
public void terminate(final RequestMonitor rm) {
// Schedule a runnable to be executed 2 seconds from now.
@ -227,9 +198,7 @@ public class GDBControl extends AbstractMIControl implements IGDBControl {
final Future<?> quitTimeoutFuture = getExecutor().schedule(
new DsfRunnable() {
public void run() {
if (!isGDBExited()) {
destroy();
}
fMIBackend.destroy();
rm.done();
}
@ -240,16 +209,15 @@ public class GDBControl extends AbstractMIControl implements IGDBControl {
},
2, TimeUnit.SECONDS);
MIGDBExit cmd = new MIGDBExit(fControlDmc);
queueCommand(
cmd,
new MIGDBExit(fControlDmc),
new DataRequestMonitor<MIInfo>(getExecutor(), rm) {
@Override
public void handleCompleted() {
// Cancel the time out runnable (if it hasn't run yet).
if (quitTimeoutFuture.cancel(false)) {
if (!isSuccess() && !isGDBExited()) {
destroy();
if (!isSuccess()) {
fMIBackend.destroy();
}
rm.done();
}
@ -276,7 +244,7 @@ public class GDBControl extends AbstractMIControl implements IGDBControl {
// Tell GDB to use this PTY
queueCommand(
new MIInferiorTTYSet(fControlDmc, fPty.getSlaveName()),
new MIInferiorTTYSet((ICommandControlDMContext)fControlDmc, fPty.getSlaveName()),
new DataRequestMonitor<MIInfo>(getExecutor(), requestMonitor) {
@Override
protected void handleFailure() {
@ -396,9 +364,9 @@ public class GDBControl extends AbstractMIControl implements IGDBControl {
*/
public void createInferiorProcess() {
if (fPty == null) {
fInferiorProcess = new GDBInferiorProcess(GDBControl.this, fProcess.getOutputStream());
fInferiorProcess = new GDBInferiorProcess(GDBControl.this, fMIBackend, fMIBackend.getMIOutputStream());
} else {
fInferiorProcess = new GDBInferiorProcess(GDBControl.this, fPty);
fInferiorProcess = new GDBInferiorProcess(GDBControl.this, fMIBackend, fPty);
}
}
@ -409,10 +377,6 @@ public class GDBControl extends AbstractMIControl implements IGDBControl {
public void setConnected(boolean connected) {
fConnected = connected;
}
public Process getGDBProcess() {
return fProcess;
}
public AbstractCLIProcess getCLIProcess() {
return fCLIProcess;
@ -422,72 +386,28 @@ public class GDBControl extends AbstractMIControl implements IGDBControl {
return fInferiorProcess;
}
public boolean isGDBExited() {
return fMonitorJob != null && fMonitorJob.fExited;
}
public int getGDBExitCode() {
return fGDBExitValue;
}
public IPath getExecutablePath() { return fExecPath; }
public IPath getExecutablePath() { return fMIBackend.getProgramPath(); }
@DsfServiceEventHandler
public void eventDispatched(ICommandControlShutdownDMEvent e) {
// Handle our "GDB Exited" event and stop processing commands.
stopCommandProcessing();
}
/**
* Monitors a system process, waiting for it to terminate, and
* then notifies the associated runtime process.
*/
private class MonitorJob extends Job {
boolean fExited = false;
DsfRunnable fMonitorStarted;
Process fMonProcess;
@Override
protected IStatus run(IProgressMonitor monitor) {
synchronized(fMonProcess) {
getExecutor().submit(fMonitorStarted);
while (!fExited) {
try {
fMonProcess.waitFor();
fGDBExitValue = fMonProcess.exitValue();
} catch (InterruptedException ie) {
// clear interrupted state
Thread.interrupted();
} finally {
fExited = true;
getSession().dispatchEvent(new GDBControlShutdownDMEvent(fControlDmc) {}, getProperties());
}
}
}
return Status.OK_STATUS;
@DsfServiceEventHandler
public void eventDispatched(BackendStateChangedEvent e) {
if (e.getState() == IMIBackend.State.TERMINATED && e.getBackendId().equals(fMIBackend.getId())) {
// Handle "GDB Exited" event, just relay to following event.
getSession().dispatchEvent(new GDBControlShutdownDMEvent(fControlDmc), getProperties());
}
MonitorJob(Process process, DsfRunnable monitorStarted) {
super("GDB process monitor job."); //$NON-NLS-1$
fMonProcess = process;
fMonitorStarted = monitorStarted;
setSystem(true);
}
void kill() {
synchronized(fMonProcess) {
if (!fExited) {
getThread().interrupt();
}
}
}
}
}
public static class InitializationShutdownStep extends Sequence.Step {
public enum Direction { INITIALIZING, SHUTTING_DOWN }
private Direction fDirection;
InitializationShutdownStep(Direction direction) { fDirection = direction; }
public InitializationShutdownStep(Direction direction) { fDirection = direction; }
@Override
final public void execute(RequestMonitor requestMonitor) {
@ -514,161 +434,13 @@ public class GDBControl extends AbstractMIControl implements IGDBControl {
requestMonitor.done();
}
}
protected class GDBProcessStep extends InitializationShutdownStep {
GDBProcessStep(Direction direction) { super(direction); }
@Override
public void initialize(final RequestMonitor requestMonitor) {
class GDBLaunchMonitor {
boolean fLaunched = false;
boolean fTimedOut = false;
}
final GDBLaunchMonitor fGDBLaunchMonitor = new GDBLaunchMonitor();
final RequestMonitor gdbLaunchRequestMonitor = new RequestMonitor(getExecutor(), null) {
@Override
protected void handleCompleted() {
if (!fGDBLaunchMonitor.fTimedOut) {
fGDBLaunchMonitor.fLaunched = true;
if (!isSuccess()) {
requestMonitor.setStatus(getStatus());
}
requestMonitor.done();
}
}
};
final Job startGdbJob = new Job("Start GDB Process Job") { //$NON-NLS-1$
@Override
protected IStatus run(IProgressMonitor monitor) {
List<String> commandList = new ArrayList<String>();
// The goal here is to keep options to an absolute minimum.
// All configuration should be done in the launch sequence
// to allow for easy overriding.
commandList.add(fGdbPath.toOSString());
commandList.add("--interpreter"); //$NON-NLS-1$
// We currently work with MI version 2
commandList.add("mi2"); //$NON-NLS-1$
// Don't read the gdbinit file here. It is read explicitly in
// the LaunchSequence to make it easier to customize.
commandList.add("--nx"); //$NON-NLS-1$
String[] commandLine = commandList.toArray(new String[commandList.size()]);
try {
fProcess = ProcessFactory.getFactory().exec(commandLine);
} catch(IOException e) {
String message = "Error while launching command " + commandList.toString(); //$NON-NLS-1$
gdbLaunchRequestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, message, e));
gdbLaunchRequestMonitor.done();
return Status.OK_STATUS;
}
try {
InputStream stream = fProcess.getInputStream();
Reader r = new InputStreamReader(stream);
BufferedReader reader = new BufferedReader(r);
String line;
while ((line = reader.readLine()) != null) {
line = line.trim();
//System.out.println("GDB " + line);
if (line.endsWith("(gdb)")) { //$NON-NLS-1$
break;
}
}
} catch (IOException e) {
gdbLaunchRequestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Error reading GDB STDOUT", e)); //$NON-NLS-1$
gdbLaunchRequestMonitor.done();
return Status.OK_STATUS;
}
gdbLaunchRequestMonitor.done();
return Status.OK_STATUS;
}
};
startGdbJob.schedule();
getExecutor().schedule(new Runnable() {
public void run() {
// Only process the event if we have not finished yet (hit the breakpoint).
if (!fGDBLaunchMonitor.fLaunched) {
fGDBLaunchMonitor.fTimedOut = true;
Thread jobThread = startGdbJob.getThread();
if (jobThread != null) {
jobThread.interrupt();
}
requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, DebugException.TARGET_REQUEST_FAILED, "Timed out trying to launch GDB.", null)); //$NON-NLS-1$
requestMonitor.done();
}
}},
fGDBLaunchTimeout, TimeUnit.SECONDS);
}
@Override
protected void shutdown(final RequestMonitor requestMonitor) {
new Job("Terminating GDB process.") { //$NON-NLS-1$
@Override
protected IStatus run(IProgressMonitor monitor) {
fProcess.destroy();
int attempts = 0;
while (attempts < 10) {
try {
// Don't know if we really need the exit value... but what the hell.
fGDBExitValue = fProcess.exitValue(); // throws exception if process not exited
requestMonitor.done();
return Status.OK_STATUS;
} catch (IllegalThreadStateException ie) {
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
attempts++;
}
requestMonitor.setStatus(new Status(
IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "Process terminate failed", null)); //$NON-NLS-1$
requestMonitor.done();
return Status.OK_STATUS;
}
}.schedule();
}
}
protected class MonitorJobStep extends InitializationShutdownStep {
MonitorJobStep(Direction direction) { super(direction); }
@Override
public void initialize(final RequestMonitor requestMonitor) {
fMonitorJob = new MonitorJob(
fProcess,
new DsfRunnable() {
public void run() {
requestMonitor.done();
}
});
fMonitorJob.schedule();
}
@Override
protected void shutdown(RequestMonitor requestMonitor) {
if (!fMonitorJob.fExited) {
fMonitorJob.kill();
}
requestMonitor.done();
}
}
protected class CommandMonitoringStep extends InitializationShutdownStep {
CommandMonitoringStep(Direction direction) { super(direction); }
@Override
protected void initialize(final RequestMonitor requestMonitor) {
startCommandProcessing(fProcess.getInputStream(), fProcess.getOutputStream());
startCommandProcessing(fMIBackend.getMIInputStream(), fMIBackend.getMIOutputStream());
requestMonitor.done();
}
@ -699,7 +471,7 @@ public class GDBControl extends AbstractMIControl implements IGDBControl {
@Override
public void initialize(final RequestMonitor requestMonitor) {
try {
fCLIProcess = new GDBCLIProcess(GDBControl.this);
fCLIProcess = new MIBackendCLIProcess(GDBControl.this, fMIBackend);
}
catch(IOException e) {
requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "Failed to create CLI Process", e)); //$NON-NLS-1$

View file

@ -12,28 +12,17 @@
*******************************************************************************/
package org.eclipse.dd.gdb.internal.provisional.service.command;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
import org.eclipse.cdt.utils.pty.PTY;
import org.eclipse.cdt.utils.spawner.ProcessFactory;
import org.eclipse.cdt.utils.spawner.Spawner;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.dd.dsf.concurrent.DataRequestMonitor;
import org.eclipse.dd.dsf.concurrent.DsfRunnable;
import org.eclipse.dd.dsf.concurrent.IDsfStatusConstants;
@ -50,12 +39,16 @@ import org.eclipse.dd.dsf.service.DsfSession;
import org.eclipse.dd.gdb.internal.GdbPlugin;
import org.eclipse.dd.gdb.internal.provisional.launching.GdbLaunch;
import org.eclipse.dd.gdb.internal.provisional.launching.LaunchUtils;
import org.eclipse.dd.gdb.internal.provisional.service.IGDBBackend;
import org.eclipse.dd.gdb.internal.provisional.service.SessionType;
import org.eclipse.dd.mi.service.IMIBackend;
import org.eclipse.dd.mi.service.IMIProcesses;
import org.eclipse.dd.mi.service.MIProcesses;
import org.eclipse.dd.mi.service.IMIBackend.BackendStateChangedEvent;
import org.eclipse.dd.mi.service.command.AbstractCLIProcess;
import org.eclipse.dd.mi.service.command.AbstractMIControl;
import org.eclipse.dd.mi.service.command.CLIEventProcessor_7_0;
import org.eclipse.dd.mi.service.command.MIBackendCLIProcess;
import org.eclipse.dd.mi.service.command.MIControlDMContext;
import org.eclipse.dd.mi.service.command.MIInferiorProcess;
import org.eclipse.dd.mi.service.command.MIRunControlEventProcessor_7_0;
@ -74,7 +67,7 @@ import org.osgi.framework.BundleContext;
/**
* GDB Debugger control implementation. This implementation extends the
* base MI control implementation to provide the GDB-specific debugger
* features. This includes:<br * - Launching and monitoring the GDB process,<br>
* features. This includes:<br>
* - CLI console support,<br>
* - inferior process status tracking.<br>
*/
@ -92,7 +85,7 @@ public class GDBControl_7_0 extends AbstractMIControl implements IGDBControl {
}
/**
* Event indicating that the back end process has terminated.
* Event indicating that the CommandControl (back end process) has terminated.
*/
private static class GDBControlShutdownDMEvent extends AbstractDMEvent<ICommandControlDMContext>
implements ICommandControlShutdownDMEvent
@ -102,22 +95,16 @@ public class GDBControl_7_0 extends AbstractMIControl implements IGDBControl {
}
}
private static int fgInstanceCounter = 0;
private final GDBControlDMContext fControlDmc;
private GDBControlDMContext fControlDmc;
private SessionType fSessionType;
private boolean fAttach;
private IGDBBackend fMIBackend;
private boolean fConnected = true;
private MonitorJob fMonitorJob;
private IPath fGdbPath;
private IPath fExecPath;
private Process fProcess;
private int fGDBExitValue;
private int fGDBLaunchTimeout = 30;
private MIRunControlEventProcessor_7_0 fMIEventProcessor;
private CLIEventProcessor_7_0 fCLICommandProcessor;
private AbstractCLIProcess fCLIProcess;
@ -126,16 +113,9 @@ public class GDBControl_7_0 extends AbstractMIControl implements IGDBControl {
private PTY fPty;
public GDBControl_7_0(DsfSession session, ILaunchConfiguration config) {
super(session, "gdbcontrol[" + ++fgInstanceCounter + "]", true); //$NON-NLS-1$ //$NON-NLS-2$
super(session, true);
fSessionType = LaunchUtils.getSessionType(config);
fAttach = LaunchUtils.getIsAttach(config);
fGdbPath = LaunchUtils.getGDBPath(config);
try {
fExecPath = LaunchUtils.verifyProgramPath(config, LaunchUtils.getCProject(config));
} catch (CoreException e) {
fExecPath = new Path(""); //$NON-NLS-1$
}
fControlDmc = new GDBControlDMContext(session.getId(), getId());
}
@Override
@ -154,9 +134,13 @@ public class GDBControl_7_0 extends AbstractMIControl implements IGDBControl {
}
public void doInitialize(final RequestMonitor requestMonitor) {
fMIBackend = getServicesTracker().getService(IGDBBackend.class);
// getId uses the MIBackend service, which is why we must wait until we
// have it, before we can create this context.
fControlDmc = new GDBControlDMContext(getSession().getId(), getId());
final Sequence.Step[] initializeSteps = new Sequence.Step[] {
new GDBProcessStep(InitializationShutdownStep.Direction.INITIALIZING),
new MonitorJobStep(InitializationShutdownStep.Direction.INITIALIZING),
new CommandMonitoringStep(InitializationShutdownStep.Direction.INITIALIZING),
new InferiorInputOutputInitStep(InitializationShutdownStep.Direction.INITIALIZING),
new CommandProcessorsStep(InitializationShutdownStep.Direction.INITIALIZING),
@ -176,8 +160,6 @@ public class GDBControl_7_0 extends AbstractMIControl implements IGDBControl {
new CommandProcessorsStep(InitializationShutdownStep.Direction.SHUTTING_DOWN),
new InferiorInputOutputInitStep(InitializationShutdownStep.Direction.SHUTTING_DOWN),
new CommandMonitoringStep(InitializationShutdownStep.Direction.SHUTTING_DOWN),
new MonitorJobStep(InitializationShutdownStep.Direction.SHUTTING_DOWN),
new GDBProcessStep(InitializationShutdownStep.Direction.SHUTTING_DOWN),
};
Sequence shutdownSequence = new Sequence(getExecutor(), requestMonitor) {
@Override public Step[] getSteps() { return shutdownSteps; }
@ -185,6 +167,10 @@ public class GDBControl_7_0 extends AbstractMIControl implements IGDBControl {
getExecutor().execute(shutdownSequence);
}
public String getId() {
return fMIBackend.getId();
}
@Override
public MIControlDMContext getControlDMContext() {
@ -202,23 +188,6 @@ public class GDBControl_7_0 extends AbstractMIControl implements IGDBControl {
public boolean getIsAttachSession() {
return fAttach;
}
public boolean canInterrupt() {
return fProcess instanceof Spawner;
}
public void interrupt() {
if (fProcess instanceof Spawner) {
Spawner gdbSpawner = (Spawner) fProcess;
gdbSpawner.interrupt();
}
}
public void destroy() {
if (fProcess instanceof Spawner) {
Spawner gdbSpawner = (Spawner) fProcess;
gdbSpawner.destroy();
}
}
public void terminate(final RequestMonitor rm) {
// Schedule a runnable to be executed 2 seconds from now.
@ -227,9 +196,7 @@ public class GDBControl_7_0 extends AbstractMIControl implements IGDBControl {
final Future<?> quitTimeoutFuture = getExecutor().schedule(
new DsfRunnable() {
public void run() {
if (!isGDBExited()) {
destroy();
}
fMIBackend.destroy();
rm.done();
}
@ -248,8 +215,8 @@ public class GDBControl_7_0 extends AbstractMIControl implements IGDBControl {
public void handleCompleted() {
// Cancel the time out runnable (if it hasn't run yet).
if (quitTimeoutFuture.cancel(false)) {
if (!isSuccess() && !isGDBExited()) {
destroy();
if (!isSuccess()) {
fMIBackend.destroy();
}
rm.done();
}
@ -276,7 +243,7 @@ public class GDBControl_7_0 extends AbstractMIControl implements IGDBControl {
// Tell GDB to use this PTY
queueCommand(
new MIInferiorTTYSet(fControlDmc, fPty.getSlaveName()),
new MIInferiorTTYSet((ICommandControlDMContext)fControlDmc, fPty.getSlaveName()),
new DataRequestMonitor<MIInfo>(getExecutor(), requestMonitor) {
@Override
protected void handleFailure() {
@ -390,9 +357,9 @@ public class GDBControl_7_0 extends AbstractMIControl implements IGDBControl {
*/
public void createInferiorProcess() {
if (fPty == null) {
fInferiorProcess = new GDBInferiorProcess(GDBControl_7_0.this, fProcess.getOutputStream());
fInferiorProcess = new GDBInferiorProcess(GDBControl_7_0.this, fMIBackend, fMIBackend.getMIOutputStream());
} else {
fInferiorProcess = new GDBInferiorProcess(GDBControl_7_0.this, fPty);
fInferiorProcess = new GDBInferiorProcess(GDBControl_7_0.this, fMIBackend, fPty);
}
}
@ -403,10 +370,6 @@ public class GDBControl_7_0 extends AbstractMIControl implements IGDBControl {
public void setConnected(boolean connected) {
fConnected = connected;
}
public Process getGDBProcess() {
return fProcess;
}
public AbstractCLIProcess getCLIProcess() {
return fCLIProcess;
@ -416,15 +379,7 @@ public class GDBControl_7_0 extends AbstractMIControl implements IGDBControl {
return fInferiorProcess;
}
public boolean isGDBExited() {
return fMonitorJob != null && fMonitorJob.fExited;
}
public int getGDBExitCode() {
return fGDBExitValue;
}
public IPath getExecutablePath() { return fExecPath; }
public IPath getExecutablePath() { return fMIBackend.getProgramPath(); }
@DsfServiceEventHandler
public void eventDispatched(ICommandControlShutdownDMEvent e) {
@ -432,56 +387,19 @@ public class GDBControl_7_0 extends AbstractMIControl implements IGDBControl {
stopCommandProcessing();
}
/**
* Monitors a system process, waiting for it to terminate, and
* then notifies the associated runtime process.
*/
private class MonitorJob extends Job {
boolean fExited = false;
DsfRunnable fMonitorStarted;
Process fMonProcess;
@Override
protected IStatus run(IProgressMonitor monitor) {
synchronized(fMonProcess) {
getExecutor().submit(fMonitorStarted);
while (!fExited) {
try {
fMonProcess.waitFor();
fGDBExitValue = fMonProcess.exitValue();
} catch (InterruptedException ie) {
// clear interrupted state
Thread.interrupted();
} finally {
fExited = true;
getSession().dispatchEvent(new GDBControlShutdownDMEvent(fControlDmc) {}, getProperties());
}
}
}
return Status.OK_STATUS;
@DsfServiceEventHandler
public void eventDispatched(BackendStateChangedEvent e) {
if (e.getState() == IMIBackend.State.TERMINATED && e.getBackendId().equals(fMIBackend.getId())) {
// Handle "GDB Exited" event, just relay to following event.
getSession().dispatchEvent(new GDBControlShutdownDMEvent(fControlDmc), getProperties());
}
MonitorJob(Process process, DsfRunnable monitorStarted) {
super("GDB process monitor job."); //$NON-NLS-1$
fMonProcess = process;
fMonitorStarted = monitorStarted;
setSystem(true);
}
void kill() {
synchronized(fMonProcess) {
if (!fExited) {
getThread().interrupt();
}
}
}
}
}
public static class InitializationShutdownStep extends Sequence.Step {
public enum Direction { INITIALIZING, SHUTTING_DOWN }
private Direction fDirection;
InitializationShutdownStep(Direction direction) { fDirection = direction; }
public InitializationShutdownStep(Direction direction) { fDirection = direction; }
@Override
final public void execute(RequestMonitor requestMonitor) {
@ -509,160 +427,12 @@ public class GDBControl_7_0 extends AbstractMIControl implements IGDBControl {
}
}
protected class GDBProcessStep extends InitializationShutdownStep {
GDBProcessStep(Direction direction) { super(direction); }
@Override
public void initialize(final RequestMonitor requestMonitor) {
class GDBLaunchMonitor {
boolean fLaunched = false;
boolean fTimedOut = false;
}
final GDBLaunchMonitor fGDBLaunchMonitor = new GDBLaunchMonitor();
final RequestMonitor gdbLaunchRequestMonitor = new RequestMonitor(getExecutor(), null) {
@Override
protected void handleCompleted() {
if (!fGDBLaunchMonitor.fTimedOut) {
fGDBLaunchMonitor.fLaunched = true;
if (!isSuccess()) {
requestMonitor.setStatus(getStatus());
}
requestMonitor.done();
}
}
};
final Job startGdbJob = new Job("Start GDB Process Job") { //$NON-NLS-1$
@Override
protected IStatus run(IProgressMonitor monitor) {
List<String> commandList = new ArrayList<String>();
// The goal here is to keep options to an absolute minimum.
// All configuration should be done in the launch sequence
// to allow for easy overriding.
commandList.add(fGdbPath.toOSString());
commandList.add("--interpreter"); //$NON-NLS-1$
// We currently work with MI version 2
commandList.add("mi2"); //$NON-NLS-1$
// Don't read the gdbinit file here. It is read explicitly in
// the LaunchSequence to make it easier to customize.
commandList.add("--nx"); //$NON-NLS-1$
String[] commandLine = commandList.toArray(new String[commandList.size()]);
try {
fProcess = ProcessFactory.getFactory().exec(commandLine);
} catch(IOException e) {
String message = "Error while launching command " + commandList.toString(); //$NON-NLS-1$
gdbLaunchRequestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, message, e));
gdbLaunchRequestMonitor.done();
return Status.OK_STATUS;
}
try {
InputStream stream = fProcess.getInputStream();
Reader r = new InputStreamReader(stream);
BufferedReader reader = new BufferedReader(r);
String line;
while ((line = reader.readLine()) != null) {
line = line.trim();
//System.out.println("GDB " + line);
if (line.endsWith("(gdb)")) { //$NON-NLS-1$
break;
}
}
} catch (IOException e) {
gdbLaunchRequestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Error reading GDB STDOUT", e)); //$NON-NLS-1$
gdbLaunchRequestMonitor.done();
return Status.OK_STATUS;
}
gdbLaunchRequestMonitor.done();
return Status.OK_STATUS;
}
};
startGdbJob.schedule();
getExecutor().schedule(new Runnable() {
public void run() {
// Only process the event if we have not finished yet (hit the breakpoint).
if (!fGDBLaunchMonitor.fLaunched) {
fGDBLaunchMonitor.fTimedOut = true;
Thread jobThread = startGdbJob.getThread();
if (jobThread != null) {
jobThread.interrupt();
}
requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, DebugException.TARGET_REQUEST_FAILED, "Timed out trying to launch GDB.", null)); //$NON-NLS-1$
requestMonitor.done();
}
}},
fGDBLaunchTimeout, TimeUnit.SECONDS);
}
@Override
protected void shutdown(final RequestMonitor requestMonitor) {
new Job("Terminating GDB process.") { //$NON-NLS-1$
@Override
protected IStatus run(IProgressMonitor monitor) {
fProcess.destroy();
int attempts = 0;
while (attempts < 10) {
try {
// Don't know if we really need the exit value... but what the hell.
fGDBExitValue = fProcess.exitValue(); // throws exception if process not exited
requestMonitor.done();
return Status.OK_STATUS;
} catch (IllegalThreadStateException ie) {
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
attempts++;
}
requestMonitor.setStatus(new Status(
IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "Process terminate failed", null)); //$NON-NLS-1$
requestMonitor.done();
return Status.OK_STATUS;
}
}.schedule();
}
}
protected class MonitorJobStep extends InitializationShutdownStep {
MonitorJobStep(Direction direction) { super(direction); }
@Override
public void initialize(final RequestMonitor requestMonitor) {
fMonitorJob = new MonitorJob(
fProcess,
new DsfRunnable() {
public void run() {
requestMonitor.done();
}
});
fMonitorJob.schedule();
}
@Override
protected void shutdown(RequestMonitor requestMonitor) {
if (!fMonitorJob.fExited) {
fMonitorJob.kill();
}
requestMonitor.done();
}
}
protected class CommandMonitoringStep extends InitializationShutdownStep {
CommandMonitoringStep(Direction direction) { super(direction); }
@Override
protected void initialize(final RequestMonitor requestMonitor) {
startCommandProcessing(fProcess.getInputStream(), fProcess.getOutputStream());
startCommandProcessing(fMIBackend.getMIInputStream(), fMIBackend.getMIOutputStream());
requestMonitor.done();
}
@ -693,7 +463,7 @@ public class GDBControl_7_0 extends AbstractMIControl implements IGDBControl {
@Override
public void initialize(final RequestMonitor requestMonitor) {
try {
fCLIProcess = new GDBCLIProcess(GDBControl_7_0.this);
fCLIProcess = new MIBackendCLIProcess(GDBControl_7_0.this, fMIBackend);
}
catch(IOException e) {
requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "Failed to create CLI Process", e)); //$NON-NLS-1$

View file

@ -18,6 +18,7 @@ import org.eclipse.cdt.utils.pty.PTY;
import org.eclipse.dd.dsf.concurrent.DsfRunnable;
import org.eclipse.dd.dsf.concurrent.ThreadSafeAndProhibitedFromDsfExecutor;
import org.eclipse.dd.dsf.debug.service.command.ICommandControlService;
import org.eclipse.dd.gdb.internal.provisional.service.IGDBBackend;
import org.eclipse.dd.mi.service.command.MIInferiorProcess;
/**
@ -25,13 +26,16 @@ import org.eclipse.dd.mi.service.command.MIInferiorProcess;
*/
class GDBInferiorProcess extends MIInferiorProcess {
private IGDBBackend fBackend;
public GDBInferiorProcess(ICommandControlService commandControl, PTY p) {
public GDBInferiorProcess(ICommandControlService commandControl, IGDBBackend backend, PTY p) {
super(commandControl, p);
fBackend = backend;
}
public GDBInferiorProcess(ICommandControlService commandControl, OutputStream gdbOutputStream) {
public GDBInferiorProcess(ICommandControlService commandControl, IGDBBackend backend, OutputStream gdbOutputStream) {
super(commandControl, gdbOutputStream);
fBackend = backend;
}
@Override
@ -54,7 +58,7 @@ class GDBInferiorProcess extends MIInferiorProcess {
if (gdb.getIsAttachSession() == false) {
// Try to interrupt the inferior, first.
if (getState() == State.RUNNING) {
gdb.interrupt();
fBackend.interrupt();
}
}
}

View file

@ -23,11 +23,6 @@ public interface IGDBControl extends ICommandControlService {
SessionType getSessionType();
boolean getIsAttachSession();
boolean canInterrupt();
void interrupt();
void destroy();
void terminate(final RequestMonitor rm);
void initInferiorInputOutput(final RequestMonitor requestMonitor);
@ -41,15 +36,9 @@ public interface IGDBControl extends ICommandControlService {
void setConnected(boolean connected);
Process getGDBProcess();
AbstractCLIProcess getCLIProcess();
MIInferiorProcess getInferiorProcess();
boolean isGDBExited();
int getGDBExitCode();
IPath getExecutablePath();
}

View file

@ -0,0 +1,91 @@
/*******************************************************************************
* Copyright (c) 2008 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Wind River Systems - initial API and implementation
* Nokia - create and use backend service.
*******************************************************************************/
package org.eclipse.dd.mi.service;
import java.io.InputStream;
import java.io.OutputStream;
import org.eclipse.dd.dsf.concurrent.Immutable;
import org.eclipse.dd.dsf.service.IDsfService;
/**
* Service for controlling the back end process.
* @since 1.1
*/
public interface IMIBackend extends IDsfService {
public enum State { NOT_INITIALIZED, STARTED, TERMINATED };
/**
* Event indicating that the back end process has started.
*/
@Immutable
public static class BackendStateChangedEvent {
final private String fSessionId;
final private String fBackendId;
final private State fState;
public BackendStateChangedEvent(String sessionId, String backendId, State state) {
fSessionId = sessionId;
fBackendId = backendId;
fState = state;
}
public String getSessionId() {
return fSessionId;
}
public String getBackendId() {
return fBackendId;
}
public State getState() {
return fState;
}
}
/**
* Returns the identifier of this backend service. It can be used
* to distinguish between multiple instances of this service in a
* single session.
*/
public String getId();
/**
* Requests that the backend be immediately terminated.
*/
public void destroy();
/**
* Returns the current state of the backed.
* @return
*/
public State getState();
/**
* Returns the exit code of the backend. Returns <code>0</code> if
* the backend exit code is not available.
* @return
*/
public int getExitCode();
/**
* Returns the backend command stream.
*/
public InputStream getMIInputStream();
/**
* Returns the backend result and event stream.
* @return
*/
public OutputStream getMIOutputStream();
}

View file

@ -8,6 +8,7 @@
* Contributors:
* Wind River Systems - initial API and implementation
* Ericsson - Modified for handling of multiple stacks and threads
* Nokia - create and use backend service.
*******************************************************************************/
package org.eclipse.dd.mi.service.command;
@ -109,21 +110,17 @@ public abstract class AbstractMIControl extends AbstractDsfService
* Flag indicating that the command control has stopped processing commands.
*/
private boolean fStoppedCommandProcessing = false;
private String fId;
public AbstractMIControl(DsfSession session) {
super(session);
fId = "<no id>"; //$NON-NLS-1$
fUseThreadAndFrameOptions = false;
}
/**
* @since 1.1
*/
public AbstractMIControl(DsfSession session, String id, boolean useThreadAndFrameOptions) {
public AbstractMIControl(DsfSession session, boolean useThreadAndFrameOptions) {
super(session);
fId = id;
fUseThreadAndFrameOptions = useThreadAndFrameOptions;
}
@ -369,13 +366,6 @@ public abstract class AbstractMIControl extends AbstractDsfService
return !fStoppedCommandProcessing;
}
/**
* @since 1.1
*/
public String getId() {
return fId;
}
/*
* These are the service routines which perform the various callouts back to the listeners.
*/

View file

@ -0,0 +1,173 @@
/*******************************************************************************
* Copyright (c) 2007, 2008 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Wind River Systems - initial API and implementation
*******************************************************************************/
package org.eclipse.dd.mi.service.command;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.dd.dsf.concurrent.ConfinedToDsfExecutor;
import org.eclipse.dd.dsf.concurrent.DataRequestMonitor;
import org.eclipse.dd.dsf.concurrent.DsfRunnable;
import org.eclipse.dd.dsf.concurrent.IDsfStatusConstants;
import org.eclipse.dd.dsf.concurrent.ImmediateExecutor;
import org.eclipse.dd.dsf.concurrent.Query;
import org.eclipse.dd.dsf.concurrent.RequestMonitor;
import org.eclipse.dd.dsf.debug.service.command.ICommandControlService;
import org.eclipse.dd.dsf.service.DsfServiceEventHandler;
import org.eclipse.dd.dsf.service.DsfSession;
import org.eclipse.dd.mi.internal.MIPlugin;
import org.eclipse.dd.mi.service.IMIBackend;
import org.eclipse.dd.mi.service.IMIBackend.BackendStateChangedEvent;
/**
* CLI Process object implementation which uses the {@link IMIBackend} service
* to monitor and control the underlying process.
*
* @since 1.1
*/
public class MIBackendCLIProcess extends AbstractCLIProcess {
private IMIBackend fMIBackend;
private AtomicInteger fExitCode = new AtomicInteger(-1);
private BackedExitedEventListener fExitedEventListener;
@ConfinedToDsfExecutor("getSession()#getExecutor")
public MIBackendCLIProcess(ICommandControlService commandControl, IMIBackend backend) throws IOException {
super(commandControl);
fMIBackend = backend;
if (fMIBackend.getState() == IMIBackend.State.TERMINATED) {
fExitCode.set(fMIBackend.getExitCode());
}
fExitedEventListener = new BackedExitedEventListener();
getSession().addServiceEventListener(fExitedEventListener, null);
}
public class BackedExitedEventListener {
private final List<RequestMonitor> fWaitForRMs = new ArrayList<RequestMonitor>();
@DsfServiceEventHandler
public void eventDispatched(BackendStateChangedEvent event) {
if (event.getState() == IMIBackend.State.TERMINATED &&
event.getBackendId().equals(fMIBackend.getId()))
{
fExitCode.set(fMIBackend.getExitCode());
for (RequestMonitor rm : fWaitForRMs) {
rm.done();
}
fWaitForRMs.clear();
}
}
void dispose() {
for (RequestMonitor rm : fWaitForRMs) {
rm.setStatus(new Status(IStatus.ERROR, MIPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "Backend terminate event never received", null)); //$NON-NLS-1$
rm.done();
}
fWaitForRMs.clear();
}
}
/**
* @see java.lang.Process#waitFor()
*/
@Override
public int waitFor() throws InterruptedException {
if (!DsfSession.isSessionActive(getSession().getId())) {
return fExitCode.get();
}
try {
Query<Object> query = new Query<Object>() {
@Override
protected void execute(final DataRequestMonitor<Object> rm) {
if ( !DsfSession.isSessionActive(getSession().getId()) ||
isDisposed() ||
fMIBackend.getState() == IMIBackend.State.TERMINATED )
{
rm.setData(new Object());
rm.done();
} else {
fExitedEventListener.fWaitForRMs.add(
new RequestMonitor(ImmediateExecutor.getInstance(), rm) {
@Override
protected void handleSuccess() {
rm.setData(new Object());
rm.done();
}
});
}
}
};
getSession().getExecutor().execute(query);
query.get();
} catch (RejectedExecutionException e) {
} catch (ExecutionException e) {
}
return fExitCode.get();
}
/**
* @see java.lang.Process#exitValue()
*/
@Override
public int exitValue() {
if (!DsfSession.isSessionActive(getSession().getId())) {
return fExitCode.get();
}
try {
getSession().getExecutor().submit(new Callable<Object>() {
public Object call() throws Exception {
if (fMIBackend.getState() != IMIBackend.State.TERMINATED) {
throw new IllegalThreadStateException("Backend Process has not exited"); //$NON-NLS-1$
}
return null;
}}).get();
} catch (RejectedExecutionException e) {
} catch (InterruptedException e) {
} catch (ExecutionException e) {
if (e.getCause() instanceof RuntimeException) {
throw (RuntimeException)e.getCause();
}
}
return fExitCode.get();
}
/**
* @see java.lang.Process#destroy()
*/
@Override
public void destroy() {
try {
getSession().getExecutor().execute(new DsfRunnable() { public void run() {
if (!DsfSession.isSessionActive(getSession().getId())) return;
if (isDisposed()) return;
fMIBackend.destroy();
}});
} catch (RejectedExecutionException e) {
// Session disposed.
}
}
@Override
public void dispose() {
fExitedEventListener.dispose();
getSession().removeServiceEventListener(fExitedEventListener);
super.dispose();
}
}

View file

@ -26,7 +26,7 @@ import org.eclipse.dd.mi.service.command.output.MIOutput;
* `Number'
* number of the breakpoint
*
* `Type'
* `State'
* type of the breakpoint: `breakpoint' or `watchpoint'
*
* `Disposition'

View file

@ -21,7 +21,7 @@ import java.util.List;
* <pre>
* ^done,BreakpointTable={nr_rows="1",nr_cols="6",hdr=[..],body=[brkpt={},brkpt={}]}
*-break-list
^done,BreakpointTable={nr_rows="6",nr_cols="6",hdr=[{width="3",alignment="-1",col_name="number",colhdr="Num"},{width="14",alignment="-1",col_name="type",colhdr="Type"},{width="4",alignment="-1",col_name="disp",colhdr="Disp"},{width="3",alignment="-1",col_name="enabled",colhdr="Enb"},{width="10",alignment="-1",col_name="addr",colhdr="Address"},{width="40",alignment="2",col_name="what",colhdr="What"}],body=[bkpt={number="1",type="breakpoint",disp="keep",enabled="y",addr="0x0804846b",func="main",file="hello.c",line="4",times="0"},bkpt={number="2",type="breakpoint",disp="del",enabled="y",addr="0x0804846b",func="main",file="hello.c",line="4",times="0"},bkpt={number="3",type="breakpoint",disp="keep",enabled="y",addr="0x0804846b",func="main",file="hello.c",line="4",cond="1",times="0"},bkpt={number="4",type="hw breakpoint",disp="keep",enabled="y",addr="0x0804846b",func="main",file="hello.c",line="4",times="0"},bkpt={number="5",type="breakpoint",disp="keep",enabled="y",addr="0x0804846b",func="main",file="hello.c",line="4",thread="0",thread="0",times="0"},bkpt={number="6",type="breakpoint",disp="keep",enabled="y",addr="0x0804846b",func="main",file="hello.c",line="4",thread="1",thread="1",times="0"}]}
^done,BreakpointTable={nr_rows="6",nr_cols="6",hdr=[{width="3",alignment="-1",col_name="number",colhdr="Num"},{width="14",alignment="-1",col_name="type",colhdr="State"},{width="4",alignment="-1",col_name="disp",colhdr="Disp"},{width="3",alignment="-1",col_name="enabled",colhdr="Enb"},{width="10",alignment="-1",col_name="addr",colhdr="Address"},{width="40",alignment="2",col_name="what",colhdr="What"}],body=[bkpt={number="1",type="breakpoint",disp="keep",enabled="y",addr="0x0804846b",func="main",file="hello.c",line="4",times="0"},bkpt={number="2",type="breakpoint",disp="del",enabled="y",addr="0x0804846b",func="main",file="hello.c",line="4",times="0"},bkpt={number="3",type="breakpoint",disp="keep",enabled="y",addr="0x0804846b",func="main",file="hello.c",line="4",cond="1",times="0"},bkpt={number="4",type="hw breakpoint",disp="keep",enabled="y",addr="0x0804846b",func="main",file="hello.c",line="4",times="0"},bkpt={number="5",type="breakpoint",disp="keep",enabled="y",addr="0x0804846b",func="main",file="hello.c",line="4",thread="0",thread="0",times="0"},bkpt={number="6",type="breakpoint",disp="keep",enabled="y",addr="0x0804846b",func="main",file="hello.c",line="4",thread="1",thread="1",times="0"}]}
* </pre>
*/
public class MIBreakListInfo extends MIInfo {