From d18ba1931354aa1304b32efafeb1b0b535fd7767 Mon Sep 17 00:00:00 2001 From: Pawel Piech Date: Tue, 14 Oct 2008 17:31:52 +0000 Subject: [PATCH] [240092] - [launch] The way to launch & configure GDB process in DSF is not customizable --- .../launch/PDAVirtualMachineVMNode.java | 2 +- .../dd/examples/pda/launch/PDALaunch.java | 4 +- .../pda/launch/PDALaunchDelegate.java | 129 ++-- .../pda/launch/PDAServicesInitSequence.java | 25 +- .../dd/examples/pda/service/PDABackend.java | 320 ++++++++++ .../examples/pda/service/PDABreakpoints.java | 14 +- .../pda/service/PDACommandControl.java | 109 ++-- .../examples/pda/service/PDAExpressions.java | 6 + .../dd/examples/pda/service/PDARegisters.java | 2 +- .../examples/pda/service/PDARunControl.java | 2 +- .../service/PDAVirtualMachineDMContext.java | 7 +- .../tests/pda/service/command/BasicTests.java | 2 +- .../command/CommandControlTestsBase.java | 43 +- .../eclipse/dd/tests/pda/util/Launching.java | 101 ++-- .../ui/actions/DsfTerminateCommand.java | 11 +- .../launching/FinalLaunchSequence.java | 98 ++- .../launching/GdbLaunchDelegate.java | 3 +- .../launching/ServicesLaunchSequence.java | 10 + .../launching/ShutdownSequence.java | 6 + .../provisional/service/GDBBackend.java | 556 ++++++++++++++++++ .../provisional/service/GDBRunControl.java | 6 +- .../service/GDBRunControl_7_0.java | 6 +- .../service/GdbDebugServicesFactory.java | 16 +- .../provisional/service/IGDBBackend.java | 96 +++ .../service/command/GDBCLIProcess.java | 102 ---- .../service/command/GDBControl.java | 312 ++-------- .../service/command/GDBControl_7_0.java | 304 ++-------- .../service/command/GDBInferiorProcess.java | 10 +- .../service/command/IGDBControl.java | 11 - .../org/eclipse/dd/mi/service/IMIBackend.java | 91 +++ .../mi/service/command/AbstractMIControl.java | 14 +- .../service/command/MIBackendCLIProcess.java | 173 ++++++ .../service/command/commands/MIBreakList.java | 2 +- .../command/output/MIBreakListInfo.java | 2 +- 34 files changed, 1584 insertions(+), 1011 deletions(-) create mode 100644 plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/examples/pda/service/PDABackend.java create mode 100644 plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/GDBBackend.java create mode 100644 plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/IGDBBackend.java delete mode 100644 plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/command/GDBCLIProcess.java create mode 100644 plugins/org.eclipse.dd.mi/src/org/eclipse/dd/mi/service/IMIBackend.java create mode 100644 plugins/org.eclipse.dd.mi/src/org/eclipse/dd/mi/service/command/MIBackendCLIProcess.java diff --git a/plugins/org.eclipse.dd.examples.pda.ui/src/org/eclipse/dd/examples/pda/ui/viewmodel/launch/PDAVirtualMachineVMNode.java b/plugins/org.eclipse.dd.examples.pda.ui/src/org/eclipse/dd/examples/pda/ui/viewmodel/launch/PDAVirtualMachineVMNode.java index 0ab91f2b14b..c6c7cb4efac 100644 --- a/plugins/org.eclipse.dd.examples.pda.ui/src/org/eclipse/dd/examples/pda/ui/viewmodel/launch/PDAVirtualMachineVMNode.java +++ b/plugins/org.eclipse.dd.examples.pda.ui/src/org/eclipse/dd/examples/pda/ui/viewmodel/launch/PDAVirtualMachineVMNode.java @@ -66,7 +66,7 @@ public class PDAVirtualMachineVMNode extends AbstractContainerVMNode return; } - update.setChild(createVMContext(commandControl.getVirtualMachineDMContext()), 0); + update.setChild(createVMContext(commandControl.getContext()), 0); update.done(); } diff --git a/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/examples/pda/launch/PDALaunch.java b/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/examples/pda/launch/PDALaunch.java index 5db49e184bc..2d8e66d9734 100644 --- a/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/examples/pda/launch/PDALaunch.java +++ b/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/examples/pda/launch/PDALaunch.java @@ -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() { diff --git a/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/examples/pda/launch/PDALaunchDelegate.java b/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/examples/pda/launch/PDALaunchDelegate.java index 96069ee9d51..5b8393125fb 100644 --- a/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/examples/pda/launch/PDALaunchDelegate.java +++ b/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/examples/pda/launch/PDALaunchDelegate.java @@ -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 commandList = new ArrayList(); - - // 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 initQuery = new Query() { @Override protected void execute(DataRequestMonitor 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 initQuery = new Query() { + @Override + protected void execute(DataRequestMonitor 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; - } } diff --git a/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/examples/pda/launch/PDAServicesInitSequence.java b/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/examples/pda/launch/PDAServicesInitSequence.java index bbdd44ed625..aae74a86358 100644 --- a/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/examples/pda/launch/PDAServicesInitSequence.java +++ b/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/examples/pda/launch/PDAServicesInitSequence.java @@ -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 diff --git a/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/examples/pda/service/PDABackend.java b/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/examples/pda/service/PDABackend.java new file mode 100644 index 00000000000..2c3801ab9bf --- /dev/null +++ b/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/examples/pda/service/PDABackend.java @@ -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()); + + 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 commandList = new ArrayList(); + + // 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; + } +} diff --git a/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/examples/pda/service/PDABreakpoints.java b/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/examples/pda/service/PDABreakpoints.java index a98a5686a06..d24aff5c22f 100644 --- a/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/examples/pda/service/PDABreakpoints.java +++ b/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/examples/pda/service/PDABreakpoints.java @@ -164,7 +164,7 @@ public class PDABreakpoints extends AbstractDsfService implements IBreakpoints public void getBreakpoints(final IBreakpointsTargetDMContext context, final DataRequestMonitor 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(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(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(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(getExecutor(), rm)); } diff --git a/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/examples/pda/service/PDACommandControl.java b/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/examples/pda/service/PDACommandControl.java index 35a3060170a..812e9a0387b 100644 --- a/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/examples/pda/service/PDACommandControl.java +++ b/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/examples/pda/service/PDACommandControl.java @@ -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 fCommandQueue = new LinkedList(); @@ -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()); - 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()); - 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()); } } diff --git a/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/examples/pda/service/PDAExpressions.java b/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/examples/pda/service/PDAExpressions.java index 0deac984758..9201dbbda79 100644 --- a/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/examples/pda/service/PDAExpressions.java +++ b/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/examples/pda/service/PDAExpressions.java @@ -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(); + } }); } diff --git a/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/examples/pda/service/PDARegisters.java b/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/examples/pda/service/PDARegisters.java index 232f2900ca2..0702f81f5f2 100644 --- a/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/examples/pda/service/PDARegisters.java +++ b/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/examples/pda/service/PDARegisters.java @@ -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. diff --git a/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/examples/pda/service/PDARunControl.java b/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/examples/pda/service/PDARunControl.java index 51201eabea3..c28e6c5ddab 100644 --- a/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/examples/pda/service/PDARunControl.java +++ b/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/examples/pda/service/PDARunControl.java @@ -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( diff --git a/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/examples/pda/service/PDAVirtualMachineDMContext.java b/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/examples/pda/service/PDAVirtualMachineDMContext.java index 5c333378470..14f388918ed 100644 --- a/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/examples/pda/service/PDAVirtualMachineDMContext.java +++ b/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/examples/pda/service/PDAVirtualMachineDMContext.java @@ -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; *

*/ 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) */ diff --git a/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/tests/pda/service/command/BasicTests.java b/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/tests/pda/service/command/BasicTests.java index 15e03f1dbd1..944a1366849 100644 --- a/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/tests/pda/service/command/BasicTests.java +++ b/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/tests/pda/service/command/BasicTests.java @@ -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 sendCommandQuery = new Query() { diff --git a/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/tests/pda/service/command/CommandControlTestsBase.java b/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/tests/pda/service/command/CommandControlTestsBase.java index 493781ca85a..941ae0b35a3 100644 --- a/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/tests/pda/service/command/CommandControlTestsBase.java +++ b/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/tests/pda/service/command/CommandControlTestsBase.java @@ -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 fEventsQueue = new LinkedBlockingQueue(); @@ -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 { @Override @@ -97,6 +103,19 @@ public class CommandControlTestsBase { fExecutor.execute(shutdownQuery); shutdownQuery.get(); } + + class ShutdownBackendServiceQuery extends Query { + @Override + protected void execute(DataRequestMonitor 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 sendCommandQuery = new Query() { diff --git a/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/tests/pda/util/Launching.java b/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/tests/pda/util/Launching.java index dfc4f3295d7..7b58ab7c193 100644 --- a/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/tests/pda/util/Launching.java +++ b/plugins/org.eclipse.dd.examples.pda/src/org/eclipse/dd/tests/pda/util/Launching.java @@ -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 commandList = new ArrayList(); - - // 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 { + @Override + protected void execute(DataRequestMonitor 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; + } } diff --git a/plugins/org.eclipse.dd.gdb.ui/src/org/eclipse/dd/gdb/internal/ui/actions/DsfTerminateCommand.java b/plugins/org.eclipse.dd.gdb.ui/src/org/eclipse/dd/gdb/internal/ui/actions/DsfTerminateCommand.java index 7d951f841fa..897c2b4f474 100644 --- a/plugins/org.eclipse.dd.gdb.ui/src/org/eclipse/dd/gdb/internal/ui/actions/DsfTerminateCommand.java +++ b/plugins/org.eclipse.dd.gdb.ui/src/org/eclipse/dd/gdb/internal/ui/actions/DsfTerminateCommand.java @@ -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(); } } diff --git a/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/launching/FinalLaunchSequence.java b/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/launching/FinalLaunchSequence.java index e21ef32c09b..26947f39481 100644 --- a/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/launching/FinalLaunchSequence.java +++ b/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/launching/FinalLaunchSequence.java @@ -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(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(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(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 p = fLaunch.getLaunchConfiguration().getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_SOLIB_PATH, - new ArrayList(1)); + List 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) { diff --git a/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/launching/GdbLaunchDelegate.java b/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/launching/GdbLaunchDelegate.java index c09b6545bba..c44642c2d96 100644 --- a/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/launching/GdbLaunchDelegate.java +++ b/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/launching/GdbLaunchDelegate.java @@ -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); diff --git a/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/launching/ServicesLaunchSequence.java b/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/launching/ServicesLaunchSequence.java index f387fb93f44..1a145848d97 100644 --- a/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/launching/ServicesLaunchSequence.java +++ b/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/launching/ServicesLaunchSequence.java @@ -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 diff --git a/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/launching/ShutdownSequence.java b/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/launching/ShutdownSequence.java index 2da3414a74f..024b16399a6 100644 --- a/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/launching/ShutdownSequence.java +++ b/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/launching/ShutdownSequence.java @@ -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) { diff --git a/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/GDBBackend.java b/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/GDBBackend.java new file mode 100644 index 00000000000..14b570f2c83 --- /dev/null +++ b/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/GDBBackend.java @@ -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.
+ *
+ * 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 fGDBCommandLine; + + /* + * Parameters for launching GDB. + */ + private IPath fProgramPath; + private IPath fGDBWorkingDirectory; + private String fGDBInitFile; + private List 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 getGDBCommandLine() { + if (fGDBCommandLine == null) + { + fGDBCommandLine = new ArrayList(); + + // 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 getSharedLibraryPaths() throws CoreException { + if (fSharedLibPaths == null) { + fSharedLibPaths = fLaunchConfiguration.getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_SOLIB_PATH, + new ArrayList(0)); + } + + return fSharedLibPaths; + } + + /* + * Launch GDB process. + * Allow subclass to override. + */ + protected Process launchGDBProcess() throws CoreException { + List 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 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()); + + /* + * 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(); + } + } + } + } + +} diff --git a/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/GDBRunControl.java b/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/GDBRunControl.java index b92409a9ce2..fca23b8fa31 100644 --- a/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/GDBRunControl.java +++ b/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/GDBRunControl.java @@ -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(), diff --git a/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/GDBRunControl_7_0.java b/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/GDBRunControl_7_0.java index 1e713542c25..a4ca10cf994 100644 --- a/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/GDBRunControl_7_0.java +++ b/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/GDBRunControl_7_0.java @@ -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()}, diff --git a/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/GdbDebugServicesFactory.java b/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/GdbDebugServicesFactory.java index 93989b1966b..00c45919f22 100644 --- a/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/GdbDebugServicesFactory.java +++ b/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/GdbDebugServicesFactory.java @@ -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 createService(Class 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); diff --git a/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/IGDBBackend.java b/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/IGDBBackend.java new file mode 100644 index 00000000000..565f8220698 --- /dev/null +++ b/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/IGDBBackend.java @@ -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.
+ *
+ * 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.
+ *
+ * Here are some special cases:
+ * 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.
+ * 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)
+ * + * @since 1.1 + */ +public interface IGDBBackend extends IMIBackend { + + + /** + * Return the command line that is used to launch GDB.
+ * Note it's not needed to put debugged binary in the command, as that will + * be set by a MI command. + * + * @return + */ + public List 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 getSharedLibraryPaths() throws CoreException; + + + /** + * Sends an interrupt signal to the GDB process. + */ + public void interrupt(); +} diff --git a/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/command/GDBCLIProcess.java b/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/command/GDBCLIProcess.java deleted file mode 100644 index 3823b1e5175..00000000000 --- a/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/command/GDBCLIProcess.java +++ /dev/null @@ -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() { - 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() { - 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. - } - } - - -} diff --git a/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/command/GDBControl.java b/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/command/GDBControl.java index 9a05c09c80d..efb573b7934 100644 --- a/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/command/GDBControl.java +++ b/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/command/GDBControl.java @@ -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:
+ * features. This includes:
* - CLI console support,
* - inferior process status tracking.
*/ @@ -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 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(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(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 commandList = new ArrayList(); - - // 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$ diff --git a/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/command/GDBControl_7_0.java b/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/command/GDBControl_7_0.java index 822eb54546c..4bc855a24e2 100644 --- a/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/command/GDBControl_7_0.java +++ b/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/command/GDBControl_7_0.java @@ -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:
+ * features. This includes:
* - CLI console support,
* - inferior process status tracking.
*/ @@ -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 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(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 commandList = new ArrayList(); - - // 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$ diff --git a/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/command/GDBInferiorProcess.java b/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/command/GDBInferiorProcess.java index 1243f70278c..d574a783905 100644 --- a/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/command/GDBInferiorProcess.java +++ b/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/command/GDBInferiorProcess.java @@ -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(); } } } diff --git a/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/command/IGDBControl.java b/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/command/IGDBControl.java index a0434116746..19f7902d21c 100644 --- a/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/command/IGDBControl.java +++ b/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/command/IGDBControl.java @@ -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(); } \ No newline at end of file diff --git a/plugins/org.eclipse.dd.mi/src/org/eclipse/dd/mi/service/IMIBackend.java b/plugins/org.eclipse.dd.mi/src/org/eclipse/dd/mi/service/IMIBackend.java new file mode 100644 index 00000000000..327296297fa --- /dev/null +++ b/plugins/org.eclipse.dd.mi/src/org/eclipse/dd/mi/service/IMIBackend.java @@ -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 0 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(); +} diff --git a/plugins/org.eclipse.dd.mi/src/org/eclipse/dd/mi/service/command/AbstractMIControl.java b/plugins/org.eclipse.dd.mi/src/org/eclipse/dd/mi/service/command/AbstractMIControl.java index 45ddd553f9a..a385775d9ed 100644 --- a/plugins/org.eclipse.dd.mi/src/org/eclipse/dd/mi/service/command/AbstractMIControl.java +++ b/plugins/org.eclipse.dd.mi/src/org/eclipse/dd/mi/service/command/AbstractMIControl.java @@ -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 = ""; //$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. */ diff --git a/plugins/org.eclipse.dd.mi/src/org/eclipse/dd/mi/service/command/MIBackendCLIProcess.java b/plugins/org.eclipse.dd.mi/src/org/eclipse/dd/mi/service/command/MIBackendCLIProcess.java new file mode 100644 index 00000000000..464850586f3 --- /dev/null +++ b/plugins/org.eclipse.dd.mi/src/org/eclipse/dd/mi/service/command/MIBackendCLIProcess.java @@ -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 fWaitForRMs = new ArrayList(); + + @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 query = new Query() { + @Override + protected void execute(final DataRequestMonitor 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() { + 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(); + } +} diff --git a/plugins/org.eclipse.dd.mi/src/org/eclipse/dd/mi/service/command/commands/MIBreakList.java b/plugins/org.eclipse.dd.mi/src/org/eclipse/dd/mi/service/command/commands/MIBreakList.java index b3472ca9f50..ab3f2a0f84f 100644 --- a/plugins/org.eclipse.dd.mi/src/org/eclipse/dd/mi/service/command/commands/MIBreakList.java +++ b/plugins/org.eclipse.dd.mi/src/org/eclipse/dd/mi/service/command/commands/MIBreakList.java @@ -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' diff --git a/plugins/org.eclipse.dd.mi/src/org/eclipse/dd/mi/service/command/output/MIBreakListInfo.java b/plugins/org.eclipse.dd.mi/src/org/eclipse/dd/mi/service/command/output/MIBreakListInfo.java index 23de9da1edf..f00bafa8cae 100644 --- a/plugins/org.eclipse.dd.mi/src/org/eclipse/dd/mi/service/command/output/MIBreakListInfo.java +++ b/plugins/org.eclipse.dd.mi/src/org/eclipse/dd/mi/service/command/output/MIBreakListInfo.java @@ -21,7 +21,7 @@ import java.util.List; *
  * ^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"}]}
  * 
*/ public class MIBreakListInfo extends MIInfo {