mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-04-23 22:52:11 +02:00
Bug 303808: Run all-stop mode with target async on GDB 7.12
The previous all-stop implementation use Ctrl-C (variants) to suspend the target program. This option is not supported when using the new GDB full CLI console provided in GDB 7.12. So the alternative is to send the mi command -exec-interrupt to suspend the target, however this option requires the use of target async-on mode (already in use for non stop mode). This update makes all-stop mode use target async mode when using versions higher or equal to GDB 7.12. This update also removes the non-stop mode restriction initially implemented for the use of the new GDB Full CLI console. Change-Id: Iabef20bdee814d413fc338f9ec8c2d99d6311c20
This commit is contained in:
parent
36fd126919
commit
31f0cc6476
15 changed files with 751 additions and 18 deletions
|
@ -0,0 +1,146 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2016 Ericsson 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
|
||||
*******************************************************************************/
|
||||
package org.eclipse.cdt.dsf.gdb.launching;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.cdt.debug.core.CDebugUtils;
|
||||
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
|
||||
import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants;
|
||||
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
|
||||
import org.eclipse.cdt.dsf.concurrent.RequestMonitorWithProgress;
|
||||
import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants;
|
||||
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
|
||||
import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.CommandFactory;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo;
|
||||
import org.eclipse.cdt.dsf.service.DsfServicesTracker;
|
||||
import org.eclipse.cdt.dsf.service.DsfSession;
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
import org.eclipse.core.runtime.Status;
|
||||
|
||||
/**
|
||||
* Subclass for GDB >= 7.12.
|
||||
*
|
||||
* @since 5.2
|
||||
*/
|
||||
public class FinalLaunchSequence_7_12 extends FinalLaunchSequence_7_7 {
|
||||
private IGDBControl fCommandControl;
|
||||
private CommandFactory fCommandFactory;
|
||||
private Map<String, Object> fAttributes;
|
||||
|
||||
public FinalLaunchSequence_7_12(DsfSession session, Map<String, Object> attributes,
|
||||
RequestMonitorWithProgress rm) {
|
||||
super(session, attributes, rm);
|
||||
fAttributes = attributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] getExecutionOrder(String group) {
|
||||
if (GROUP_TOP_LEVEL.equals(group)) {
|
||||
// Initialize the list with the base class' steps
|
||||
// We need to create a list that we can modify, which is why we create our own ArrayList.
|
||||
List<String> orderList = new ArrayList<String>(
|
||||
Arrays.asList(super.getExecutionOrder(GROUP_TOP_LEVEL)));
|
||||
|
||||
// Now insert our steps right after the initialization of the base class.
|
||||
orderList.add(orderList.indexOf("stepInitializeFinalLaunchSequence_7_7") + 1, //$NON-NLS-1$
|
||||
"stepInitializeFinalLaunchSequence_7_12"); //$NON-NLS-1$
|
||||
|
||||
orderList.add(orderList.indexOf("stepSourceGDBInitFile") + 1, //$NON-NLS-1$
|
||||
"stepSetTargetAsync"); //$NON-NLS-1$
|
||||
|
||||
orderList.add(orderList.indexOf("stepSetTargetAsync") + 1, //$NON-NLS-1$
|
||||
"stepSetRecordFullStopAtLimit"); //$NON-NLS-1$
|
||||
|
||||
return orderList.toArray(new String[orderList.size()]);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the members of the FinalLaunchSequence_7_12 class. This step is mandatory for the rest of
|
||||
* the sequence to complete.
|
||||
*/
|
||||
@Execute
|
||||
public void stepInitializeFinalLaunchSequence_7_12(RequestMonitor rm) {
|
||||
DsfServicesTracker tracker = new DsfServicesTracker(GdbPlugin.getBundleContext(),
|
||||
getSession().getId());
|
||||
fCommandControl = tracker.getService(IGDBControl.class);
|
||||
tracker.dispose();
|
||||
|
||||
if (fCommandControl == null) {
|
||||
rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR,
|
||||
"Cannot obtain service", null)); //$NON-NLS-1$
|
||||
return;
|
||||
}
|
||||
|
||||
fCommandFactory = fCommandControl.getCommandFactory();
|
||||
|
||||
rm.done();
|
||||
|
||||
}
|
||||
|
||||
@Execute
|
||||
public void stepSetTargetAsync(RequestMonitor requestMonitor) {
|
||||
// Use target async when interfacing with GDB 7.12 or higher
|
||||
// this will allow us to use the new enhanced GDB Full CLI console
|
||||
fCommandControl.queueCommand(
|
||||
fCommandFactory.createMIGDBSetTargetAsync(fCommandControl.getContext(), true),
|
||||
new DataRequestMonitor<MIInfo>(getExecutor(), requestMonitor) {
|
||||
@Override
|
||||
protected void handleError() {
|
||||
// We should only be calling this for GDB >= 7.12,
|
||||
// but just in case, accept errors for older GDBs
|
||||
requestMonitor.done();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set reverse debugging record full stop-at-limit to off, so GDB does not halt waiting for user input
|
||||
* when the recording buffer gets full
|
||||
* @param requestMonitor
|
||||
*/
|
||||
@Execute
|
||||
public void stepSetRecordFullStopAtLimit(RequestMonitor requestMonitor) {
|
||||
fCommandControl.queueCommand(
|
||||
fCommandFactory.createMIGDBSetRecordFullStopAtLimit(fCommandControl.getContext(), false),
|
||||
new DataRequestMonitor<MIInfo>(getExecutor(), requestMonitor) {
|
||||
@Override
|
||||
protected void handleError() {
|
||||
// Accept errors since this is not essential
|
||||
requestMonitor.done();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@Execute
|
||||
public void stepSetNonStop(final RequestMonitor requestMonitor) {
|
||||
boolean isNonStop = CDebugUtils.getAttribute(fAttributes,
|
||||
IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_NON_STOP,
|
||||
LaunchUtils.getIsNonStopModeDefault());
|
||||
|
||||
if (isNonStop) {
|
||||
// GDBs that don't support non-stop don't allow you to set it to false.
|
||||
// We really should set it to false when GDB supports it though.
|
||||
// Something to fix later.
|
||||
// Note that disabling pagination is taken care of elsewhere
|
||||
fCommandControl.queueCommand(
|
||||
fCommandFactory.createMIGDBSetNonStop(fCommandControl.getContext(), true),
|
||||
new DataRequestMonitor<MIInfo>(getExecutor(), requestMonitor));
|
||||
} else {
|
||||
requestMonitor.done();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,7 +14,6 @@ import java.io.OutputStream;
|
|||
|
||||
import org.eclipse.cdt.core.parser.util.StringUtil;
|
||||
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
|
||||
import org.eclipse.cdt.dsf.gdb.launching.LaunchUtils;
|
||||
import org.eclipse.cdt.dsf.service.DsfSession;
|
||||
import org.eclipse.cdt.utils.pty.PTY;
|
||||
import org.eclipse.cdt.utils.pty.PTY.Mode;
|
||||
|
@ -48,20 +47,16 @@ public class GDBBackend_7_12 extends GDBBackend {
|
|||
/** Indicate that we failed to create a PTY. */
|
||||
private boolean fPtyFailure;
|
||||
|
||||
private boolean fIsAllStop;
|
||||
|
||||
private InputStream fDummyErrorStream;
|
||||
|
||||
public GDBBackend_7_12(DsfSession session, ILaunchConfiguration lc) {
|
||||
super(session, lc);
|
||||
fIsAllStop = !LaunchUtils.getIsNonStopMode(lc);
|
||||
createPty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFullGdbConsoleSupported() {
|
||||
return !Platform.getOS().equals(Platform.OS_WIN32)
|
||||
&& !fIsAllStop
|
||||
&& !fPtyFailure;
|
||||
}
|
||||
|
||||
|
@ -159,6 +154,12 @@ public class GDBBackend_7_12 extends GDBBackend {
|
|||
// Now trigger the new console towards our PTY.
|
||||
"-ex", "new-ui mi " + fMIPty.getSlaveName(), //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
||||
// With GDB.7.12, pagination can lock up the whole debug session
|
||||
// when using the full GDB console, so we turn it off.
|
||||
// We must turn it off before calling 'show version' as even
|
||||
// that command could cause pagination to trigger
|
||||
"-ex", "set pagination off", //$NON-NLS-1$//$NON-NLS-2$
|
||||
|
||||
// Now print the version so the user gets that familiar output
|
||||
"-ex", "show version" //$NON-NLS-1$ //$NON-NLS-2$
|
||||
};
|
||||
|
|
|
@ -7,11 +7,17 @@
|
|||
*******************************************************************************/
|
||||
package org.eclipse.cdt.dsf.gdb.service;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
|
||||
import org.eclipse.cdt.dsf.concurrent.DsfExecutor;
|
||||
import org.eclipse.cdt.dsf.concurrent.ImmediateDataRequestMonitor;
|
||||
import org.eclipse.cdt.dsf.concurrent.ImmediateRequestMonitor;
|
||||
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
|
||||
import org.eclipse.cdt.dsf.concurrent.Sequence;
|
||||
import org.eclipse.cdt.dsf.datamodel.DMContexts;
|
||||
import org.eclipse.cdt.dsf.datamodel.IDMContext;
|
||||
import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext;
|
||||
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
|
||||
import org.eclipse.cdt.dsf.mi.service.IMIContainerDMContext;
|
||||
import org.eclipse.cdt.dsf.mi.service.IMIProcessDMContext;
|
||||
|
@ -29,6 +35,13 @@ public class GDBProcesses_7_12 extends GDBProcesses_7_10 {
|
|||
super(session);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Sequence getStartOrRestartProcessSequence(DsfExecutor executor,
|
||||
IContainerDMContext containerDmc, Map<String, Object> attributes, boolean restart,
|
||||
DataRequestMonitor<IContainerDMContext> rm) {
|
||||
return new StartOrRestartProcessSequence_7_12(executor, containerDmc, attributes, restart, rm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void terminate(IThreadDMContext thread, RequestMonitor rm) {
|
||||
IGDBBackend backend = getServicesTracker().getService(IGDBBackend.class);
|
||||
|
|
|
@ -74,6 +74,17 @@ public class GDBRunControl_7_10 extends GDBRunControl_7_6 implements IReverseRun
|
|||
requestMonitor.done();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReverseModeEnabled(boolean enabled) {
|
||||
super.setReverseModeEnabled(enabled);
|
||||
if (!enabled) {
|
||||
// Keep the disabled state in sync with the trace method
|
||||
// This is needed e.g. to restart reverse mode during
|
||||
// a process restart
|
||||
fReverseTraceMethod = ReverseDebugMethod.OFF;
|
||||
}
|
||||
}
|
||||
|
||||
/** @since 5.1 */
|
||||
protected void setReverseTraceMethod(ReverseDebugMethod traceMethod) {
|
||||
if (fReverseTraceMethod != traceMethod) {
|
||||
|
|
|
@ -0,0 +1,356 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2016 Ericsson 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
|
||||
*******************************************************************************/
|
||||
package org.eclipse.cdt.dsf.gdb.service;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.cdt.debug.core.model.IChangeReverseMethodHandler.ReverseDebugMethod;
|
||||
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
|
||||
import org.eclipse.cdt.dsf.concurrent.DsfRunnable;
|
||||
import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants;
|
||||
import org.eclipse.cdt.dsf.concurrent.ImmediateDataRequestMonitor;
|
||||
import org.eclipse.cdt.dsf.concurrent.ImmediateRequestMonitor;
|
||||
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
|
||||
import org.eclipse.cdt.dsf.datamodel.DMContexts;
|
||||
import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext;
|
||||
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
|
||||
import org.eclipse.cdt.dsf.mi.service.IMICommandControl;
|
||||
import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.CommandFactory;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.events.IMIDMEvent;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.events.MIBreakpointHitEvent;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.events.MIStoppedEvent;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.MIBreakpoint;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.MIFrame;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo;
|
||||
import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
|
||||
import org.eclipse.cdt.dsf.service.DsfSession;
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
import org.eclipse.core.runtime.Status;
|
||||
import org.eclipse.core.runtime.jobs.Job;
|
||||
|
||||
/**
|
||||
* @since 5.2
|
||||
*/
|
||||
public class GDBRunControl_7_12 extends GDBRunControl_7_10 {
|
||||
private IMICommandControl fCommandControl;
|
||||
private CommandFactory fCommandFactory;
|
||||
private Map<String, EnableReverseAtLocOperation> fBpIdToReverseOpMap = new HashMap<>();
|
||||
|
||||
public GDBRunControl_7_12(DsfSession session) {
|
||||
super(session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(final RequestMonitor rm) {
|
||||
super.initialize(new ImmediateRequestMonitor(rm) {
|
||||
@Override
|
||||
protected void handleSuccess() {
|
||||
doInitialize(rm);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void doInitialize(final RequestMonitor rm) {
|
||||
fCommandControl = getServicesTracker().getService(IMICommandControl.class);
|
||||
fCommandFactory = fCommandControl.getCommandFactory();
|
||||
|
||||
register(new String[]{ GDBRunControl_7_12.class.getName() },
|
||||
new Hashtable<String,String>());
|
||||
|
||||
rm.done();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void suspend(IExecutionDMContext context, final RequestMonitor rm){
|
||||
canSuspend(
|
||||
context,
|
||||
new DataRequestMonitor<Boolean>(getExecutor(), rm) {
|
||||
@Override
|
||||
protected void handleSuccess() {
|
||||
if (getData()) {
|
||||
// Thread or Process
|
||||
doSuspend(context, rm);
|
||||
} else {
|
||||
rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Context cannot be suspended.", null)); //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void doSuspend(IExecutionDMContext context, final RequestMonitor rm) {
|
||||
// Start the job before sending the interrupt command
|
||||
// to make sure we don't miss the *stopped event
|
||||
final MonitorSuspendJob monitorJob = new MonitorSuspendJob(0, rm);
|
||||
fCommandControl.queueCommand(fCommandFactory.createMIExecInterrupt(context),
|
||||
new ImmediateDataRequestMonitor<MIInfo>() {
|
||||
@Override
|
||||
protected void handleSuccess() {
|
||||
// Nothing to do in the case of success, the monitoring job
|
||||
// will take care of completing the RM once it gets the
|
||||
// *stopped event.
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleFailure() {
|
||||
// In case of failure, we must cancel the monitoring job
|
||||
// and indicate the failure in the rm.
|
||||
monitorJob.cleanAndCancel();
|
||||
rm.done(getStatus());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTargetAcceptingCommands() {
|
||||
// Async mode is on when running with GDB 7.12 or higher
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 5.2
|
||||
*/
|
||||
@DsfServiceEventHandler
|
||||
public void eventDispatched(ISuspendedDMEvent event) {
|
||||
assert event instanceof IMIDMEvent;
|
||||
|
||||
if (event instanceof IMIDMEvent) {
|
||||
Object evt = ((IMIDMEvent)event).getMIEvent();
|
||||
|
||||
if (evt instanceof MIBreakpointHitEvent) {
|
||||
MIBreakpointHitEvent miEvt = (MIBreakpointHitEvent)evt;
|
||||
|
||||
for (EnableReverseAtLocOperation enableReverse : fBpIdToReverseOpMap.values()) {
|
||||
if (breakpointHitMatchesLocation(miEvt, enableReverse)) {
|
||||
// We are now stopped at the right place to initiate the recording for reverse mode
|
||||
// Remove the operation from our internal map and process it
|
||||
fBpIdToReverseOpMap.remove(enableReverse.fBpId);
|
||||
IContainerDMContext containerContext = enableReverse.getContainerContext();
|
||||
ICommandControlDMContext controlDmc = DMContexts.getAncestorOfType(containerContext, ICommandControlDMContext.class);
|
||||
ReverseDebugMethod reverseMethod = enableReverse.getReverseDebugMethod();
|
||||
if (controlDmc != null && reverseMethod != null) {
|
||||
enableReverseMode(controlDmc, reverseMethod, new RequestMonitor(getExecutor(), null) {
|
||||
@Override
|
||||
protected void handleSuccess() {
|
||||
if (enableReverse.shouldTriggerContinue()) {
|
||||
fCommandControl.queueCommand(fCommandFactory.createMIExecContinue(containerContext),
|
||||
new ImmediateDataRequestMonitor<MIInfo>());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Not expecting more than one operation for the same location
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class EnableReverseAtLocOperation {
|
||||
private final IContainerDMContext fContainerContext;
|
||||
private final ReverseDebugMethod fTraceMethod;
|
||||
private final String fBpId;
|
||||
private final String fFileLocation;
|
||||
private final String fAddrLocation;
|
||||
private final boolean fTriggerContinue;
|
||||
|
||||
public EnableReverseAtLocOperation(IContainerDMContext containerContext, ReverseDebugMethod traceMethod,
|
||||
String bpId, String fileLoc, String addr, boolean tiggerContinue) {
|
||||
fContainerContext = containerContext;
|
||||
fTraceMethod = traceMethod;
|
||||
fBpId = bpId;
|
||||
fFileLocation = fileLoc;
|
||||
fAddrLocation = addr;
|
||||
fTriggerContinue = tiggerContinue;
|
||||
}
|
||||
|
||||
public IContainerDMContext getContainerContext() {
|
||||
return fContainerContext;
|
||||
}
|
||||
|
||||
public ReverseDebugMethod getReverseDebugMethod() {
|
||||
return fTraceMethod;
|
||||
}
|
||||
|
||||
public String getBreakointId() {
|
||||
return fBpId;
|
||||
}
|
||||
|
||||
public String getFileLocation() {
|
||||
return fFileLocation;
|
||||
}
|
||||
|
||||
public String getAddrLocation() {
|
||||
return fAddrLocation;
|
||||
}
|
||||
|
||||
public boolean shouldTriggerContinue() {
|
||||
return fTriggerContinue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return fBpId.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (other instanceof EnableReverseAtLocOperation) {
|
||||
if (fContainerContext != null
|
||||
&& fContainerContext.equals(((EnableReverseAtLocOperation) other).fContainerContext)
|
||||
&& fTraceMethod != null
|
||||
&& fTraceMethod.equals(((EnableReverseAtLocOperation) other).fTraceMethod)
|
||||
&& fBpId != null
|
||||
&& fBpId.equals(((EnableReverseAtLocOperation) other).fBpId)
|
||||
&& fFileLocation != null
|
||||
&& fFileLocation.equals(((EnableReverseAtLocOperation) other).fFileLocation)
|
||||
&& fAddrLocation != null
|
||||
&& fAddrLocation.equals(((EnableReverseAtLocOperation) other).fAddrLocation)
|
||||
&& fTriggerContinue == ((EnableReverseAtLocOperation) other).fTriggerContinue) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the reverse debugging method as soon as the program is suspended at the specified breakpoint location
|
||||
*
|
||||
* It is recommended to use this request before the program runs or restarts in order to prevent timing issues and
|
||||
* miss a suspend event
|
||||
*
|
||||
* Note, using the break point id to determine the stop location would be sufficient although in the case where
|
||||
* multiple break points are inserted in the same location, GDB will only report one of them (e.g. GDB 7.12)
|
||||
*
|
||||
* Having the MIBreakpoint will give us access to the address, file and line number as well which can be used as
|
||||
* alternatives to determine a matched location.
|
||||
*
|
||||
* This method is specially useful when using async mode with i.e. with GDB 7.12.
|
||||
* Activating reverse debugging when the target is running may trigger an unresponsive GDB, this triggered the
|
||||
* creation of this method
|
||||
*
|
||||
*/
|
||||
void enableReverseModeAtBpLocation(final IContainerDMContext containerContext, final ReverseDebugMethod traceMethod,
|
||||
MIBreakpoint bp, boolean triggerContinue) {
|
||||
|
||||
// Using an internal convention for file location i.e. file:lineNumber
|
||||
String fileLoc = bp.getFile() + ":" + bp.getLine(); //$NON-NLS-1$
|
||||
|
||||
fBpIdToReverseOpMap.put(bp.getNumber(), new EnableReverseAtLocOperation(containerContext, traceMethod,
|
||||
bp.getNumber(), fileLoc, bp.getAddress(), triggerContinue));
|
||||
}
|
||||
|
||||
private boolean breakpointHitMatchesLocation(MIBreakpointHitEvent e, EnableReverseAtLocOperation enableReverse) {
|
||||
if (enableReverse != null) {
|
||||
String bpId = e.getNumber();
|
||||
|
||||
// Here we check three different things to see if we are stopped at the right place
|
||||
// 1- The actual location in the file. But this does not work for breakpoints that
|
||||
// were set on non-executable lines
|
||||
// 2- The address where the breakpoint was set. But this does not work for breakpoints
|
||||
// that have multiple addresses (GDB returns <MULTIPLE>.) I think that is for multi-process
|
||||
// 3- The breakpoint id that was hit. But this does not work if another breakpoint
|
||||
// was also set on the same line because GDB may return that breakpoint as being hit.
|
||||
//
|
||||
// So this works for the large majority of cases. The case that won't work is when the user
|
||||
// does a runToLine to a line that is non-executable AND has another breakpoint AND
|
||||
// has multiple addresses for the breakpoint. I'm mean, come on!
|
||||
boolean equalFileLocation = false;
|
||||
boolean equalAddrLocation = false;
|
||||
boolean equalBpId = bpId.equals(enableReverse.getBreakointId());
|
||||
MIFrame frame = e.getFrame();
|
||||
if(frame != null) {
|
||||
String fileLocation = frame.getFile() + ":" + frame.getLine(); //$NON-NLS-1$
|
||||
String addrLocation = frame.getAddress();
|
||||
equalFileLocation = fileLocation.equals(enableReverse.getFileLocation());
|
||||
equalAddrLocation = addrLocation.equals(enableReverse.getAddrLocation());
|
||||
}
|
||||
|
||||
if (equalFileLocation || equalAddrLocation || equalBpId) {
|
||||
// We stopped at the right place
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected class MonitorSuspendJob extends Job {
|
||||
// Bug 310274. Until we have a preference to configure timeouts,
|
||||
// we need a large enough default timeout to accommodate slow
|
||||
// remote sessions.
|
||||
private final static int TIMEOUT_DEFAULT_VALUE = 5000;
|
||||
|
||||
private final RequestMonitor fRequestMonitor;
|
||||
|
||||
public MonitorSuspendJob(int timeout, RequestMonitor rm) {
|
||||
super("Suspend monitor job."); //$NON-NLS-1$
|
||||
setSystem(true);
|
||||
fRequestMonitor = rm;
|
||||
|
||||
if (timeout <= 0) {
|
||||
timeout = TIMEOUT_DEFAULT_VALUE; // default of 5 seconds
|
||||
}
|
||||
|
||||
// Register to listen for the stopped event
|
||||
getSession().addServiceEventListener(this, null);
|
||||
|
||||
schedule(timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup job and cancel it.
|
||||
* This method is required because super.canceling() is only called
|
||||
* if the job is actually running.
|
||||
*/
|
||||
public boolean cleanAndCancel() {
|
||||
if (getExecutor().isInExecutorThread()) {
|
||||
getSession().removeServiceEventListener(this);
|
||||
} else {
|
||||
getExecutor().submit(
|
||||
new DsfRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
getSession().removeServiceEventListener(MonitorSuspendJob.this);
|
||||
}
|
||||
});
|
||||
}
|
||||
return cancel();
|
||||
}
|
||||
|
||||
@DsfServiceEventHandler
|
||||
public void eventDispatched(MIStoppedEvent e) {
|
||||
if (e.getDMContext() != null && e.getDMContext() instanceof IMIExecutionDMContext ) {
|
||||
// For all-stop, this means all threads have stopped
|
||||
if (cleanAndCancel()) {
|
||||
fRequestMonitor.done();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IStatus run(IProgressMonitor monitor) {
|
||||
// This will be called when the timeout is hit and no *stopped event was received
|
||||
getExecutor().submit(
|
||||
new DsfRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
getSession().removeServiceEventListener(MonitorSuspendJob.this);
|
||||
fRequestMonitor.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "Suspend operation timeout.", null)); //$NON-NLS-1$
|
||||
}
|
||||
});
|
||||
return Status.OK_STATUS;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -45,6 +45,7 @@ import org.eclipse.cdt.dsf.gdb.launching.LaunchUtils;
|
|||
import org.eclipse.cdt.dsf.gdb.service.command.CommandFactory_6_8;
|
||||
import org.eclipse.cdt.dsf.gdb.service.command.GDBControl;
|
||||
import org.eclipse.cdt.dsf.gdb.service.command.GDBControl_7_0;
|
||||
import org.eclipse.cdt.dsf.gdb.service.command.GDBControl_7_12;
|
||||
import org.eclipse.cdt.dsf.gdb.service.command.GDBControl_7_2;
|
||||
import org.eclipse.cdt.dsf.gdb.service.command.GDBControl_7_4;
|
||||
import org.eclipse.cdt.dsf.gdb.service.command.GDBControl_7_7;
|
||||
|
@ -212,6 +213,9 @@ public class GdbDebugServicesFactory extends AbstractDsfDebugServicesFactory {
|
|||
}
|
||||
|
||||
protected ICommandControl createCommandControl(DsfSession session, ILaunchConfiguration config) {
|
||||
if (compareVersionWith(GDB_7_12_VERSION) >= 0) {
|
||||
return new GDBControl_7_12(session, config, new CommandFactory_6_8());
|
||||
}
|
||||
if (compareVersionWith(GDB_7_7_VERSION) >= 0) {
|
||||
return new GDBControl_7_7(session, config, new CommandFactory_6_8());
|
||||
}
|
||||
|
@ -318,6 +322,9 @@ public class GdbDebugServicesFactory extends AbstractDsfDebugServicesFactory {
|
|||
}
|
||||
|
||||
// Else, handle all-stop mode
|
||||
if (compareVersionWith(GDB_7_12_VERSION) >= 0) {
|
||||
return new GDBRunControl_7_12(session);
|
||||
}
|
||||
if (compareVersionWith(GDB_7_10_VERSION) >= 0) {
|
||||
return new GDBRunControl_7_10(session);
|
||||
}
|
||||
|
|
|
@ -80,6 +80,7 @@ public class StartOrRestartProcessSequence_7_0 extends ReflectionSequence {
|
|||
// on the main() method.
|
||||
private boolean fUserBreakpointIsOnMain;
|
||||
|
||||
private MIBreakpoint fBreakPointForReverse;
|
||||
private boolean fReverseEnabled;
|
||||
private final Map<String, Object> fAttributes;
|
||||
|
||||
|
@ -101,6 +102,13 @@ public class StartOrRestartProcessSequence_7_0 extends ReflectionSequence {
|
|||
return fUserBreakpoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 5.2
|
||||
*/
|
||||
protected MIBreakpoint getBreakPointForReverse() {
|
||||
return fBreakPointForReverse;
|
||||
}
|
||||
|
||||
protected boolean getUserBreakpointIsOnMain() {
|
||||
return fUserBreakpointIsOnMain;
|
||||
}
|
||||
|
@ -243,8 +251,11 @@ public class StartOrRestartProcessSequence_7_0 extends ReflectionSequence {
|
|||
public void handleSuccess() {
|
||||
if (getData() != null) {
|
||||
MIBreakpoint[] breakpoints = getData().getMIBreakpoints();
|
||||
if (breakpoints.length > 0 && fUserBreakpoint != null) {
|
||||
fUserBreakpointIsOnMain = breakpoints[0].getAddress().equals(fUserBreakpoint.getAddress());
|
||||
if (breakpoints.length > 0) {
|
||||
fBreakPointForReverse = breakpoints[0];
|
||||
if (fUserBreakpoint != null) {
|
||||
fUserBreakpointIsOnMain = fBreakPointForReverse.getAddress().equals(fUserBreakpoint.getAddress());
|
||||
}
|
||||
}
|
||||
}
|
||||
rm.done();
|
||||
|
|
|
@ -112,6 +112,12 @@ public class StartOrRestartProcessSequence_7_10 extends StartOrRestartProcessSeq
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 5.2
|
||||
*/
|
||||
protected ReverseDebugMethod getReverseMode() {
|
||||
return fReverseMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Here we set the reverse debug mode
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2016 Ericsson 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
|
||||
*******************************************************************************/
|
||||
package org.eclipse.cdt.dsf.gdb.service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
|
||||
import org.eclipse.cdt.dsf.concurrent.DsfExecutor;
|
||||
import org.eclipse.cdt.dsf.concurrent.ImmediateRequestMonitor;
|
||||
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
|
||||
import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext;
|
||||
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.MIBreakpoint;
|
||||
import org.eclipse.cdt.dsf.service.DsfServicesTracker;
|
||||
|
||||
/**
|
||||
* @since 5.2
|
||||
*/
|
||||
public class StartOrRestartProcessSequence_7_12 extends StartOrRestartProcessSequence_7_10 {
|
||||
private GDBRunControl_7_12 fReverseService;
|
||||
|
||||
public StartOrRestartProcessSequence_7_12(DsfExecutor executor, IContainerDMContext containerDmc,
|
||||
Map<String, Object> attributes, boolean restart, DataRequestMonitor<IContainerDMContext> rm) {
|
||||
super(executor, containerDmc, attributes, restart, rm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the members of the StartOrRestartProcessSequence_7_12 class.
|
||||
* This step is mandatory for the rest of the sequence to complete.
|
||||
*/
|
||||
@Override
|
||||
@Execute
|
||||
public void stepInitializeBaseSequence(final RequestMonitor rm) {
|
||||
super.stepInitializeBaseSequence(new ImmediateRequestMonitor(rm) {
|
||||
@Override
|
||||
protected void handleSuccess() {
|
||||
DsfServicesTracker tracker = new DsfServicesTracker(GdbPlugin.getBundleContext(),
|
||||
getContainerContext().getSessionId());
|
||||
fReverseService = tracker.getService(GDBRunControl_7_12.class);
|
||||
tracker.dispose();
|
||||
rm.done();
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] getExecutionOrder(String group) {
|
||||
if (GROUP_TOP_LEVEL.equals(group)) {
|
||||
// Initialize the list with the base class' steps
|
||||
// We need to create a list that we can modify, which is why we create our own ArrayList.
|
||||
List<String> orderList = new ArrayList<String>(Arrays.asList(super.getExecutionOrder(GROUP_TOP_LEVEL)));
|
||||
|
||||
// Need to insert reverse mode off before ordering the reverse start at a specified location
|
||||
orderList.add(orderList.indexOf("stepCreateConsole") + 1, "stepSetReverseOff2"); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
||||
// Order the activation of reverse debugging before starting the program, it will be executed once the
|
||||
// program stops at the specified location.
|
||||
orderList.add(orderList.indexOf("stepSetReverseOff2") + 1, "stepSetReverseModeAtLocation"); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
||||
return orderList.toArray(new String[orderList.size()]);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Execute
|
||||
public void stepSetReverseOff2(RequestMonitor rm) {
|
||||
super.stepSetReverseOff(rm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Request the enabling of reverse debugging, it will be applied once the program
|
||||
* stops at the breakpoint inserted for reverse debugging
|
||||
*/
|
||||
@Execute
|
||||
public void stepSetReverseModeAtLocation(final RequestMonitor rm) {
|
||||
MIBreakpoint bp = getBreakPointForReverse();
|
||||
if (getReverseEnabled() && fReverseService != null && bp != null) {
|
||||
// Order to continue execution if there is no user break point inserted at main
|
||||
fReverseService.enableReverseModeAtBpLocation(getContainerContext(), getReverseMode(), bp,
|
||||
!getUserBreakpointIsOnMain());
|
||||
}
|
||||
rm.done();
|
||||
}
|
||||
|
||||
/*
|
||||
* We have scheduled the start of reverse debug, so we shall skip this method from super class
|
||||
*/
|
||||
@Override
|
||||
@Execute
|
||||
public void stepSetReverseMode(RequestMonitor rm) {
|
||||
rm.done();
|
||||
}
|
||||
|
||||
/*
|
||||
* We have scheduled the start of reverse debug, so we shall skip this method from super class
|
||||
*/
|
||||
@Override
|
||||
@Execute
|
||||
public void stepEnableReverse(RequestMonitor rm) {
|
||||
rm.done();
|
||||
}
|
||||
|
||||
/*
|
||||
* The order to continue or not has been included with the order to start reverse debugging at a specific location
|
||||
* so we shall skip this method from super class
|
||||
*/
|
||||
@Override
|
||||
@Execute
|
||||
public void stepContinue(RequestMonitor rm) {
|
||||
rm.done();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2016 Ericsson 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
|
||||
*******************************************************************************/
|
||||
package org.eclipse.cdt.dsf.gdb.service.command;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.cdt.dsf.concurrent.RequestMonitorWithProgress;
|
||||
import org.eclipse.cdt.dsf.concurrent.Sequence;
|
||||
import org.eclipse.cdt.dsf.gdb.launching.FinalLaunchSequence_7_12;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.CommandFactory;
|
||||
import org.eclipse.cdt.dsf.service.DsfSession;
|
||||
import org.eclipse.debug.core.ILaunchConfiguration;
|
||||
|
||||
/**
|
||||
* Need a new FinalLaunchSequence for GDB 7.12
|
||||
* @since 5.2
|
||||
*/
|
||||
public class GDBControl_7_12 extends GDBControl_7_7 {
|
||||
public GDBControl_7_12(DsfSession session, ILaunchConfiguration config, CommandFactory factory) {
|
||||
super(session, config, factory);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Sequence getCompleteInitializationSequence(Map<String, Object> attributes, RequestMonitorWithProgress rm) {
|
||||
return new FinalLaunchSequence_7_12(getSession(), attributes, rm);
|
||||
}
|
||||
}
|
|
@ -9,7 +9,7 @@ package org.eclipse.cdt.dsf.gdb.service.extensions;
|
|||
|
||||
import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService;
|
||||
import org.eclipse.cdt.dsf.gdb.service.GdbDebugServicesFactory;
|
||||
import org.eclipse.cdt.dsf.gdb.service.command.GDBControl_7_7;
|
||||
import org.eclipse.cdt.dsf.gdb.service.command.GDBControl_7_12;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.CommandFactory;
|
||||
import org.eclipse.cdt.dsf.service.DsfSession;
|
||||
import org.eclipse.debug.core.ILaunchConfiguration;
|
||||
|
@ -38,14 +38,14 @@ import org.eclipse.debug.core.ILaunchConfiguration;
|
|||
*
|
||||
* @since 4.8
|
||||
*/
|
||||
public class GDBControl_HEAD extends GDBControl_7_7 {
|
||||
public class GDBControl_HEAD extends GDBControl_7_12 {
|
||||
public GDBControl_HEAD(DsfSession session, ILaunchConfiguration lc, CommandFactory factory) {
|
||||
super(session, lc, factory);
|
||||
|
||||
validateGdbVersion(session);
|
||||
}
|
||||
|
||||
protected String getMinGDBVersionSupported() { return GdbDebugServicesFactory.GDB_7_7_VERSION; }
|
||||
protected String getMinGDBVersionSupported() { return GdbDebugServicesFactory.GDB_7_12_VERSION; }
|
||||
|
||||
protected void validateGdbVersion(DsfSession session) {
|
||||
GdbDebugServicesFactory.validateGdbVersion(session, getMinGDBVersionSupported(), this);
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
package org.eclipse.cdt.dsf.gdb.service.extensions;
|
||||
|
||||
import org.eclipse.cdt.dsf.debug.service.IRunControl;
|
||||
import org.eclipse.cdt.dsf.gdb.service.GDBRunControl_7_10;
|
||||
import org.eclipse.cdt.dsf.gdb.service.GDBRunControl_7_12;
|
||||
import org.eclipse.cdt.dsf.gdb.service.GdbDebugServicesFactory;
|
||||
import org.eclipse.cdt.dsf.service.DsfSession;
|
||||
|
||||
|
@ -37,14 +37,14 @@ import org.eclipse.cdt.dsf.service.DsfSession;
|
|||
*
|
||||
* @since 4.8
|
||||
*/
|
||||
public class GDBRunControl_HEAD extends GDBRunControl_7_10 {
|
||||
public class GDBRunControl_HEAD extends GDBRunControl_7_12 {
|
||||
public GDBRunControl_HEAD(DsfSession session) {
|
||||
super(session);
|
||||
|
||||
validateGdbVersion(session);
|
||||
}
|
||||
|
||||
protected String getMinGDBVersionSupported() { return GdbDebugServicesFactory.GDB_7_10_VERSION; }
|
||||
protected String getMinGDBVersionSupported() { return GdbDebugServicesFactory.GDB_7_12_VERSION; }
|
||||
|
||||
protected void validateGdbVersion(DsfSession session) {
|
||||
GdbDebugServicesFactory.validateGdbVersion(session, getMinGDBVersionSupported(), this);
|
||||
|
|
|
@ -135,6 +135,7 @@ import org.eclipse.cdt.dsf.mi.service.command.commands.MIGDBSetPagination;
|
|||
import org.eclipse.cdt.dsf.mi.service.command.commands.MIGDBSetPrintObject;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.commands.MIGDBSetPrintSevenbitStrings;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.commands.MIGDBSetPythonPrintStack;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.commands.MIGDBSetRecordFullStopAtLimit;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.commands.MIGDBSetSchedulerLocking;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.commands.MIGDBSetSolibAbsolutePrefix;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.commands.MIGDBSetSolibSearchPath;
|
||||
|
@ -821,6 +822,11 @@ public class CommandFactory {
|
|||
return new MIGDBSetPythonPrintStack(ctx, option);
|
||||
}
|
||||
|
||||
/** @since 5.2 */
|
||||
public ICommand<MIInfo> createMIGDBSetRecordFullStopAtLimit(ICommandControlDMContext ctx, boolean isSet) {
|
||||
return new MIGDBSetRecordFullStopAtLimit(ctx, isSet);
|
||||
}
|
||||
|
||||
/** @since 4.1 */
|
||||
public ICommand<MIInfo> createMIGDBSetSchedulerLocking(ICommandControlDMContext ctx, String mode) {
|
||||
return new MIGDBSetSchedulerLocking(ctx, mode);
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2016 Ericsson 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
|
||||
*******************************************************************************/
|
||||
package org.eclipse.cdt.dsf.mi.service.command.commands;
|
||||
|
||||
import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext;
|
||||
|
||||
/**
|
||||
* -gdb-set record full stop-at-limit [on | off]
|
||||
* @since 5.2
|
||||
*
|
||||
*/
|
||||
public class MIGDBSetRecordFullStopAtLimit extends MIGDBSet
|
||||
{
|
||||
public MIGDBSetRecordFullStopAtLimit(ICommandControlDMContext ctx, boolean isSet) {
|
||||
super(ctx, new String[] {"record", "full", "stop-at-limit", isSet ? "on" : "off"});//$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
|
||||
}
|
||||
}
|
|
@ -19,7 +19,7 @@ import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants;
|
|||
import org.eclipse.cdt.dsf.concurrent.ImmediateDataRequestMonitor;
|
||||
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
|
||||
import org.eclipse.cdt.dsf.concurrent.RequestMonitorWithProgress;
|
||||
import org.eclipse.cdt.dsf.gdb.launching.FinalLaunchSequence_7_7;
|
||||
import org.eclipse.cdt.dsf.gdb.launching.FinalLaunchSequence_7_12;
|
||||
import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo;
|
||||
import org.eclipse.cdt.dsf.service.DsfServicesTracker;
|
||||
|
@ -29,7 +29,7 @@ import org.eclipse.cdt.examples.dsf.gdb.service.IGDBExtendedFunctions;
|
|||
import org.eclipse.core.runtime.IStatus;
|
||||
import org.eclipse.core.runtime.Status;
|
||||
|
||||
public class GdbExtendedFinalLaunchSequence extends FinalLaunchSequence_7_7 {
|
||||
public class GdbExtendedFinalLaunchSequence extends FinalLaunchSequence_7_12 {
|
||||
|
||||
private IGDBControl fControl;
|
||||
private DsfServicesTracker fTracker;
|
||||
|
|
Loading…
Add table
Reference in a new issue