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:
parent
bb30f026fc
commit
d18ba19313
34 changed files with 1584 additions and 1011 deletions
|
@ -66,7 +66,7 @@ public class PDAVirtualMachineVMNode extends AbstractContainerVMNode
|
|||
return;
|
||||
}
|
||||
|
||||
update.setChild(createVMContext(commandControl.getVirtualMachineDMContext()), 0);
|
||||
update.setChild(createVMContext(commandControl.getContext()), 0);
|
||||
update.done();
|
||||
}
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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)
|
||||
*/
|
||||
|
|
|
@ -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>() {
|
||||
|
|
|
@ -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>() {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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(),
|
||||
|
|
|
@ -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()},
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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$
|
||||
|
|
|
@ -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$
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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'
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Add table
Reference in a new issue