| /******************************************************************************* |
| * Copyright (c) 2006, 2016 Wind River Systems and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * 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, //$NON-NLS-1$ |
| selectedFunction, rm); |
| } |
| }); |
| } |
| |
| /** |
| * 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(); |
| } |
| } |
| |
| } |