/******************************************************************************* * Copyright (c) 2006, 2016 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 * Ericsson AB - Modified for additional functionality * Nokia - create and use backend service. * Alvaro Sanchez-Leon (Ericsson AB) - Support for Step into selection (bug 244865) * Alvaro Sanchez-Leon (Ericsson AB) - Bug 415362 *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.service; import java.util.Arrays; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import org.eclipse.cdt.core.model.IFunctionDeclaration; import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; 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.datamodel.IDMContext; import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext; import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext; import org.eclipse.cdt.dsf.debug.service.IProcesses.IThreadDMContext; import org.eclipse.cdt.dsf.debug.service.IRunControl; import org.eclipse.cdt.dsf.debug.service.IRunControl2; import org.eclipse.cdt.dsf.debug.service.IRunControl3; import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; import org.eclipse.cdt.dsf.gdb.internal.service.control.StepIntoSelectionActiveOperation; import org.eclipse.cdt.dsf.gdb.internal.service.control.StepIntoSelectionUtils; import org.eclipse.cdt.dsf.mi.service.IMICommandControl; import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext; import org.eclipse.cdt.dsf.mi.service.IMIProcessDMContext; import org.eclipse.cdt.dsf.mi.service.IMIProcesses; import org.eclipse.cdt.dsf.mi.service.IMIRunControl; import org.eclipse.cdt.dsf.mi.service.MIRunControl; import org.eclipse.cdt.dsf.mi.service.MIStack; import org.eclipse.cdt.dsf.mi.service.command.CommandFactory; import org.eclipse.cdt.dsf.mi.service.command.events.MIBreakpointHitEvent; import org.eclipse.cdt.dsf.mi.service.command.events.MIEvent; import org.eclipse.cdt.dsf.mi.service.command.events.MIInferiorExitEvent; import org.eclipse.cdt.dsf.mi.service.command.events.MIRunningEvent; import org.eclipse.cdt.dsf.mi.service.command.events.MISignalEvent; import org.eclipse.cdt.dsf.mi.service.command.events.MIStoppedEvent; import org.eclipse.cdt.dsf.mi.service.command.events.MIThreadExitEvent; import org.eclipse.cdt.dsf.mi.service.command.output.MIBreakInsertInfo; 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.mi.service.command.output.MIStackInfoDepthInfo; import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; import org.eclipse.cdt.dsf.service.DsfSession; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; public class GDBRunControl extends MIRunControl { private static class RunToLineActiveOperation { private IMIExecutionDMContext fThreadContext; private String fBpId; private String fFileLocation; private String fAddrLocation; private boolean fSkipBreakpoints; public RunToLineActiveOperation(IMIExecutionDMContext threadContext, String bpId, String fileLoc, String addr, boolean skipBreakpoints) { fThreadContext = threadContext; fBpId = bpId; fFileLocation = fileLoc; fAddrLocation = addr; fSkipBreakpoints = skipBreakpoints; } public IMIExecutionDMContext getThreadContext() { return fThreadContext; } public String getBreakointId() { return fBpId; } public String getFileLocation() { return fFileLocation; } public String getAddrLocation() { return fAddrLocation; } public boolean shouldSkipBreakpoints() { return fSkipBreakpoints; } } private IGDBBackend fGdb; private IMIProcesses fProcService; private CommandFactory fCommandFactory; private ICommandControlService fConnection; // Record list of execution contexts private IExecutionDMContext[] fOldExecutionCtxts; private RunToLineActiveOperation fRunToLineActiveOperation = null; private StepIntoSelectionActiveOperation fStepInToSelectionActiveOperation = null; public GDBRunControl(DsfSession session) { super(session); } @Override public void initialize(final RequestMonitor requestMonitor) { super.initialize( new ImmediateRequestMonitor(requestMonitor) { @Override public void handleSuccess() { doInitialize(requestMonitor); }}); } private void doInitialize(final RequestMonitor requestMonitor) { fGdb = getServicesTracker().getService(IGDBBackend.class); fProcService = getServicesTracker().getService(IMIProcesses.class); fCommandFactory = getServicesTracker().getService(IMICommandControl.class).getCommandFactory(); fConnection = getServicesTracker().getService(ICommandControlService.class); register(new String[]{IRunControl.class.getName(), IRunControl2.class.getName(), IRunControl3.class.getName(), IMIRunControl.class.getName(), MIRunControl.class.getName(), GDBRunControl.class.getName()}, new Hashtable<String,String>()); requestMonitor.done(); } @Override public void shutdown(final RequestMonitor requestMonitor) { unregister(); super.shutdown(requestMonitor); } /** * Allows us to know if run control operations should be * enabled or disabled. Run control operations are * disabled, for example, when dealing with post-mortem sessions. * @since 4.2 */ protected boolean runControlOperationsEnabled() { return fGdb.getSessionType() != SessionType.CORE; } @Override public IMIExecutionDMContext createMIExecutionContext(IContainerDMContext container, String threadId) { IProcessDMContext procDmc = DMContexts.getAncestorOfType(container, IProcessDMContext.class); IThreadDMContext threadDmc = null; if (procDmc != null) { // For now, reuse the threadId as the OSThreadId threadDmc = fProcService.createThreadContext(procDmc, threadId); } return fProcService.createExecutionContext(container, threadDmc, threadId); } @Override public void suspend(final IExecutionDMContext context, final RequestMonitor rm){ canSuspend( context, new DataRequestMonitor<Boolean>(getExecutor(), rm) { @Override protected void handleSuccess() { if (getData()) { int interruptTimeout = getInterruptTimeout(); // A local Windows attach session requires us to interrupt // the inferior instead of gdb. This deficiency was fixed // in gdb 7.0. Note that there's a GDBRunControl_7_0, // so we're dealing with gdb < 7.0 if we get here. // Refer to https://bugs.eclipse.org/bugs/show_bug.cgi?id=304096#c56 // if you have the stomach for it. if (fGdb.getIsAttachSession() && fGdb.getSessionType() != SessionType.REMOTE && Platform.getOS().equals(Platform.OS_WIN32)) { IMIProcessDMContext processDmc = DMContexts.getAncestorOfType(context, IMIProcessDMContext.class); String inferiorPid = processDmc.getProcId(); if (inferiorPid != null) { fGdb.interruptInferiorAndWait(Long.parseLong(inferiorPid), interruptTimeout, rm); } else { assert false : "why don't we have the inferior's pid?"; //$NON-NLS-1$ fGdb.interruptAndWait(interruptTimeout, rm); } } else { fGdb.interruptAndWait(interruptTimeout, rm); } } else { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Context cannot be suspended.", null)); //$NON-NLS-1$ rm.done(); } } }); } /* * This is a HACK. Remove this method when GDB starts to account exited threads id in -thread-list-id command. * Exited threads are reported in -thread-list-id command even after an exit event is raised by GDB * Hence, this method needs a special handling in case of GDB. * Raises ExitEvent when a thread really exits from the system. This is done by comparing the execution contexts list * See bug 200615 for details. */ @Override public void getExecutionContexts(IContainerDMContext containerDmc, final DataRequestMonitor<IExecutionDMContext[]> rm) { fProcService.getProcessesBeingDebugged( containerDmc, new DataRequestMonitor<IDMContext[]>(getExecutor(), rm) { @Override protected void handleSuccess() { if (getData() instanceof IExecutionDMContext[]) { IExecutionDMContext[] execDmcs = (IExecutionDMContext[])getData(); raiseExitEvents(execDmcs); fOldExecutionCtxts = execDmcs; rm.setData(fOldExecutionCtxts); } else { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid contexts", null)); //$NON-NLS-1$ } rm.done(); } }); } private void raiseExitEvents(IExecutionDMContext[] ctxts){ if(ctxts == null || fOldExecutionCtxts == null) return; List<IExecutionDMContext> list = Arrays.asList(ctxts); List<IExecutionDMContext> oldThreadList = Arrays.asList(fOldExecutionCtxts); Iterator<IExecutionDMContext> iterator = oldThreadList.iterator(); while(iterator.hasNext()){ IExecutionDMContext ctxt = iterator.next(); if(! list.contains(ctxt)){ IContainerDMContext containerDmc = DMContexts.getAncestorOfType(ctxt, IContainerDMContext.class); MIEvent<?> e = new MIThreadExitEvent(containerDmc, ((IMIExecutionDMContext)ctxt).getThreadId()); // Dispatch DsfMIThreadExitEvent getSession().dispatchEvent(e, getProperties()); } } } /** * @since 2.0 */ @Override public void canResume(IExecutionDMContext context, DataRequestMonitor<Boolean> rm) { if (!runControlOperationsEnabled()) { rm.setData(false); rm.done(); return; } super.canResume(context, rm); } /** * @since 2.0 */ @Override public void canSuspend(IExecutionDMContext context, DataRequestMonitor<Boolean> rm) { if (!runControlOperationsEnabled()) { rm.setData(false); rm.done(); return; } super.canSuspend(context, rm); } /** * @since 2.0 */ @Override public void canStep(final IExecutionDMContext context, StepType stepType, final DataRequestMonitor<Boolean> rm) { if (!runControlOperationsEnabled()) { rm.setData(false); rm.done(); return; } if (context instanceof IContainerDMContext) { rm.setData(false); rm.done(); return; } if (stepType == StepType.STEP_RETURN) { // A step return will always be done in the top stack frame. // If the top stack frame is the only stack frame, it does not make sense // to do a step return since GDB will reject it. MIStack stackService = getServicesTracker().getService(MIStack.class); if (stackService != null) { // Check that the stack is at least two deep. stackService.getStackDepth(context, 2, new DataRequestMonitor<Integer>(getExecutor(), rm) { @Override public void handleCompleted() { if (isSuccess() && getData() == 1) { rm.setData(false); rm.done(); } else { canResume(context, rm); } } }); return; } } canResume(context, rm); } /** @since 3.0 */ @Override public void runToLocation(IExecutionDMContext context, final String location, final boolean skipBreakpoints, final RequestMonitor rm){ assert context != null; final IMIExecutionDMContext dmc = DMContexts.getAncestorOfType(context, IMIExecutionDMContext.class); if (dmc == null){ rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Given context: " + context + " is not an execution context.", null)); //$NON-NLS-1$ //$NON-NLS-2$ rm.done(); return; } if (doCanResume(dmc)) { IBreakpointsTargetDMContext bpDmc = DMContexts.getAncestorOfType(context, IBreakpointsTargetDMContext.class); getConnection().queueCommand( fCommandFactory.createMIBreakInsert(bpDmc, true, false, null, 0, location, dmc.getThreadId()), new DataRequestMonitor<MIBreakInsertInfo>(getExecutor(), rm) { @Override public void handleSuccess() { // We must set are RunToLineActiveOperation *before* we do the resume // or else we may get the stopped event, before we have set this variable. String bpId = getData().getMIBreakpoints()[0].getNumber(); String addr = getData().getMIBreakpoints()[0].getAddress(); fRunToLineActiveOperation = new RunToLineActiveOperation(dmc, bpId, location, addr, skipBreakpoints); resume(dmc, new RequestMonitor(getExecutor(), rm) { @Override public void handleFailure() { IBreakpointsTargetDMContext bpDmc = DMContexts.getAncestorOfType(fRunToLineActiveOperation.getThreadContext(), IBreakpointsTargetDMContext.class); String bpId = fRunToLineActiveOperation.getBreakointId(); getConnection().queueCommand(fCommandFactory.createMIBreakDelete(bpDmc, new String[] {bpId}), new DataRequestMonitor<MIInfo>(getExecutor(), null)); fRunToLineActiveOperation = null; fStepInToSelectionActiveOperation = null; super.handleFailure(); } }); } }); } else { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Cannot resume given DMC.", null)); //$NON-NLS-1$ rm.done(); } } /** * @nooverride This method is not intended to be re-implemented or extended by clients. * @noreference This method is not intended to be referenced by clients. * * @since 2.0 */ @DsfServiceEventHandler public void eventDispatched(MIInferiorExitEvent e) { if (fRunToLineActiveOperation != null) { IBreakpointsTargetDMContext bpDmc = DMContexts.getAncestorOfType(fRunToLineActiveOperation.getThreadContext(), IBreakpointsTargetDMContext.class); String bpId = fRunToLineActiveOperation.getBreakointId(); getConnection().queueCommand(fCommandFactory.createMIBreakDelete(bpDmc, new String[] {bpId}), new DataRequestMonitor<MIInfo>(getExecutor(), null)); fRunToLineActiveOperation = null; fStepInToSelectionActiveOperation = null; } } /** @since 2.0 */ @Override @DsfServiceEventHandler public void eventDispatched(final MIStoppedEvent e) { // A disabled signal event is due to interrupting the target // to set a breakpoint. This can happen during a run-to-line // or step-into operation, so we need to check it first. if (fDisableNextSignalEvent && e instanceof MISignalEvent) { fDisableNextSignalEvent = false; fSilencedSignalEvent = e; // We don't broadcast this stopped event return; } if (processRunToLineStoppedEvent(e)) { // If RunToLine is not completed return; } if (!processStepIntoSelection(e)) { //Step into Selection is not in progress broadcast the stop event super.eventDispatched(e); } } /* (non-Javadoc) * @see org.eclipse.cdt.dsf.mi.service.MIRunControl#eventDispatched(org.eclipse.cdt.dsf.mi.service.command.events.MIRunningEvent) */ @Override @DsfServiceEventHandler public void eventDispatched(final MIRunningEvent e) { if (fDisableNextRunningEvent) { // Leave the action to the super class super.eventDispatched(e); return; } if (fRunToLineActiveOperation == null && fStepInToSelectionActiveOperation == null) { // No special case here, i.e. send notification super.eventDispatched(e); } else { // Either RuntoLine or StepIntoSelection operations are active if (fLatestEvent instanceof ISuspendedDMEvent) { // Need to send out Running event notification only once per operation, then a stop event is expected at // the end of it super.eventDispatched(e); } } // No event dispatched if RuntoLine or StepIntoSelection operations are active and a previous event is not a // Suspended event, i.e. only one Running event distributed per operation } private boolean processRunToLineStoppedEvent(final MIStoppedEvent e) { if (fRunToLineActiveOperation != null) { String bpId = ""; //$NON-NLS-1$ if (e instanceof MIBreakpointHitEvent) { bpId = ((MIBreakpointHitEvent)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(fRunToLineActiveOperation.getBreakointId()); MIFrame frame = e.getFrame(); if(frame != null) { String fileLocation = frame.getFile() + ":" + frame.getLine(); //$NON-NLS-1$ String addrLocation = frame.getAddress(); equalFileLocation = fileLocation.equals(fRunToLineActiveOperation.getFileLocation()); equalAddrLocation = addrLocation.equals(fRunToLineActiveOperation.getAddrLocation()); } if (equalFileLocation || equalAddrLocation || equalBpId) { // We stopped at the right place. All is well. fRunToLineActiveOperation = null; } else { // Didn't stop at the right place yet if (fRunToLineActiveOperation.shouldSkipBreakpoints() && e instanceof MIBreakpointHitEvent) { getConnection().queueCommand( fCommandFactory.createMIExecContinue(fRunToLineActiveOperation.getThreadContext()), new DataRequestMonitor<MIInfo>(getExecutor(), null)); // Don't send the stop event since we are resuming again. return true; } else { // Stopped at another breakpoint that we should not skip. // Or got an interrupt signal from a suspend command. // Or got an interrupt signal because the user set/changed a breakpoint. This last case is tricky. // We could let the run-to-line continue its job, however, I'm thinking that if the user creates // a new breakpoint, she may want to force the program to stop, in a way to abort the run-to-line. // So, let's cancel the run-to-line in this case. // // Just remove our temporary one since we don't want it to hit later IBreakpointsTargetDMContext bpDmc = DMContexts.getAncestorOfType(fRunToLineActiveOperation.getThreadContext(), IBreakpointsTargetDMContext.class); getConnection().queueCommand(fCommandFactory.createMIBreakDelete(bpDmc, new String[] {fRunToLineActiveOperation.getBreakointId()}), new DataRequestMonitor<MIInfo>(getExecutor(), null)); fRunToLineActiveOperation = null; fStepInToSelectionActiveOperation = null; } } } return false; } private boolean processStepIntoSelection(final MIStoppedEvent e) { if (fStepInToSelectionActiveOperation == null) { return false; } // First check if it is the right thread that stopped final IMIExecutionDMContext threadDmc = DMContexts.getAncestorOfType(e.getDMContext(), IMIExecutionDMContext.class); if (fStepInToSelectionActiveOperation.getThreadContext().equals(threadDmc)) { final MIFrame frame = e.getFrame(); assert(fRunToLineActiveOperation == null); if (fStepInToSelectionActiveOperation.getRunToLineFrame() == null) { assert(fStepInToSelectionActiveOperation.getLine() == frame.getLine()); // Shall now be at the runToline location fStepInToSelectionActiveOperation.setRunToLineFrame(frame); } // Step - Not at the right place just yet // Initiate an async call chain parent getStackDepth(threadDmc, new DataRequestMonitor<Integer>(getExecutor(), null) { private int originalStackDepth = fStepInToSelectionActiveOperation.getOriginalStackDepth(); @Override protected void handleSuccess() { int frameDepth = getStackDepth(); if (frameDepth > originalStackDepth) { //shall be true as this is using stepinto step type vs instruction stepinto assert(frameDepth == originalStackDepth + 1); // Check for a match if (StepIntoSelectionUtils.sameSignature(frame, fStepInToSelectionActiveOperation)) { // Hit !! stopStepIntoSelection(e); return; } // Located deeper in the stack, Shall continue step / search // Step return continueStepping(e, StepType.STEP_RETURN); } else if (frameDepth == originalStackDepth) { // Continue step / search as long as // this is the starting base line for the search String currentLocation = frame.getFile() + ":" + frame.getLine(); //$NON-NLS-1$ String searchLineLocation = fStepInToSelectionActiveOperation.getFileLocation(); if (currentLocation.equals(searchLineLocation)) { continueStepping(e, StepType.STEP_INTO); } else { // We have moved to a line // different from the base // search line i.e. missed the // target function !! StepIntoSelectionUtils.missedSelectedTarget(fStepInToSelectionActiveOperation); stopStepIntoSelection(e); } } else { // missed the target point StepIntoSelectionUtils.missedSelectedTarget(fStepInToSelectionActiveOperation); } } @Override protected void handleFailure() { // log error if (getStatus() != null) { GdbPlugin.getDefault().getLog().log(getStatus()); } stopStepIntoSelection(e); } private int getStackDepth() { Integer stackDepth = null; if (isSuccess() && getData() != null) { stackDepth = getData(); // This is the base frame, the original stack depth shall be updated if (frame == fStepInToSelectionActiveOperation.getRunToLineFrame()) { fStepInToSelectionActiveOperation.setOriginalStackDepth(stackDepth); originalStackDepth = stackDepth; } } if (stackDepth == null) { // Unsuccessful resolution of stack depth, default to same stack depth to detect a change of line within the original frame return fStepInToSelectionActiveOperation.getOriginalStackDepth(); } return stackDepth.intValue(); } }); //Processing step into selection return true; } //All threads stopped, however outside the scope of the step into selection context //We need to abort the step into selection and broadcast the stop fStepInToSelectionActiveOperation = null; return false; } private void stopStepIntoSelection(final MIStoppedEvent e) { fStepInToSelectionActiveOperation = null; // Need to broadcast the stop super.eventDispatched(e); } /** * @since 4.2 */ protected int getInterruptTimeout() { return IGDBBackend.INTERRUPT_TIMEOUT_DEFAULT; } private void continueStepping(final MIStoppedEvent event, StepType steptype) { step(fStepInToSelectionActiveOperation.getThreadContext(), steptype, false, new RequestMonitor(getExecutor(), null) { @Override protected void handleFailure() { // log error if (getStatus() != null) { GdbPlugin.getDefault().getLog().log(getStatus()); } stopStepIntoSelection(event); } }); } // ------------------------------------------------------------------------ // Step into Selection // ------------------------------------------------------------------------ private void stepIntoSelection(final IExecutionDMContext context, final int baseLine, final String baseLineLocation, final boolean skipBreakpoints, final IFunctionDeclaration targetFunction, final RequestMonitor rm) { assert context != null; final IMIExecutionDMContext dmc = DMContexts.getAncestorOfType(context, IMIExecutionDMContext.class); if (dmc == null) { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Given context: " + context + " is not an MI execution context.", null)); //$NON-NLS-1$ //$NON-NLS-2$ rm.done(); return; } if (!doCanResume(dmc)) { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Cannot resume context", null)); //$NON-NLS-1$ rm.done(); return; } if (fLatestEvent == null || !(fLatestEvent instanceof SuspendedEvent)) { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Given context: " + context + " invalid suspended event.", null)); //$NON-NLS-1$ //$NON-NLS-2$ rm.done(); return; } SuspendedEvent suspendedEvent = (SuspendedEvent) fLatestEvent; final MIFrame currentFrame = suspendedEvent.getMIEvent().getFrame(); if (currentFrame == null) { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Given event: " + suspendedEvent + " with invalid frame.", null)); //$NON-NLS-1$ //$NON-NLS-2$ rm.done(); return; } getStackDepth(dmc, new DataRequestMonitor<Integer>(getExecutor(), rm) { @Override public void handleSuccess() { if (getData() != null) { final int framesSize = getData().intValue(); // make sure the operation is removed upon // failure detection final RequestMonitor rms = new RequestMonitor(getExecutor(), rm) { @Override protected void handleFailure() { fStepInToSelectionActiveOperation = null; super.handleFailure(); } }; if ((currentFrame.getFile() + ":" + currentFrame.getLine()).endsWith(baseLineLocation)) { //$NON-NLS-1$ // Save the step into selection information fStepInToSelectionActiveOperation = new StepIntoSelectionActiveOperation(dmc, baseLine, targetFunction, framesSize, currentFrame); // Ready to step into a function selected // within a current line step(dmc, StepType.STEP_INTO, rms); } else { // Save the step into selection information fStepInToSelectionActiveOperation = new StepIntoSelectionActiveOperation(dmc, baseLine, targetFunction, framesSize, null); // Pointing to a line different than the current line // Needs to RunToLine before stepping to the selection runToLocation(dmc, baseLineLocation, skipBreakpoints, rms); } } else { rm.done(); } } }); } /** * @since 4.2 */ @Override public void canStepIntoSelection(IExecutionDMContext context, String sourceFile, int lineNumber, IFunctionDeclaration selectedFunction, DataRequestMonitor<Boolean> rm) { canStep(context, StepType.STEP_INTO, rm); } /** * @since 4.2 */ @Override public void stepIntoSelection(final IExecutionDMContext context, String sourceFile, final int lineNumber, final boolean skipBreakpoints, final IFunctionDeclaration selectedFunction, final RequestMonitor rm) { determineDebuggerPath(context, sourceFile, new ImmediateDataRequestMonitor<String>(rm) { @Override protected void handleSuccess() { stepIntoSelection(context, lineNumber, getData() + ":" + Integer.toString(lineNumber), skipBreakpoints, selectedFunction, rm); //$NON-NLS-1$ } }); } /** * Help method used when the stopped event has not been broadcasted e.g. in the middle of step into selection * * @param dmc * @param rm */ private void getStackDepth(final IMIExecutionDMContext dmc, final DataRequestMonitor<Integer> rm) { if (dmc != null) { fConnection.queueCommand(fCommandFactory.createMIStackInfoDepth(dmc), new DataRequestMonitor<MIStackInfoDepthInfo>(getExecutor(), rm) { @Override protected void handleSuccess() { rm.setData(getData().getDepth()); rm.done(); } }); } else { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context", null)); //$NON-NLS-1$ rm.done(); } } }