private int Continue(IDebugThread2 pThread) { ThrowIfDisposed(); if (_firstContinue) { _firstContinue = false; } else { // If _sentContinue is true, then this is a dummy Continue issued to notify the // debugger that user has explicitly entered something at the Browse prompt, and // we don't actually need to issue the command to R debugger. Func <CancellationToken, Task> continueMethod = null; lock (_browseLock) { if (_sentContinue != true) { _sentContinue = true; continueMethod = ct => DebugSession.ContinueAsync(ct); } } if (continueMethod != null) { TaskExtensions.RunSynchronouslyOnUIThread(continueMethod); } } return(VSConstants.S_OK); }
internal DebugStackFrame(DebugSession session, int index, DebugStackFrame callingFrame, JObject jFrame, DebugStackFrame fallbackFrame = null) { Session = session; Index = index; CallingFrame = callingFrame; FileName = jFrame.Value <string>("filename"); LineNumber = jFrame.Value <int?>("line_number"); Call = jFrame.Value <string>("call"); IsGlobal = jFrame.Value <bool?>("is_global") ?? false; var match = _doTraceRegex.Match(Call); if (match.Success) { FrameKind = DebugStackFrameKind.DoTrace; } if (fallbackFrame != null) { // If we still don't have the filename and line number, use those from the fallback frame. // This happens during breakpoint hit processing after the context is unwound from within // .doTrace back to the function that called it - because we no longer have .doTrace call, // we don't have the file/line information that came from it. But DebugSession will have // stashed it away when it had it, and then pass it as a fallback frame if index matches. FileName = FileName ?? fallbackFrame.FileName; LineNumber = LineNumber ?? fallbackFrame.LineNumber; } }
int IDebugEngine2.CauseBreak() { ThrowIfDisposed(); DebugSession.BreakAsync() .SilenceException <MessageTransportException>() .DoNotWait(); return(VSConstants.S_OK); }
int IDebugEngine2.Attach(IDebugProgram2[] rgpPrograms, IDebugProgramNode2[] rgpProgramNodes, uint celtPrograms, IDebugEventCallback2 pCallback, enum_ATTACH_REASON dwReason) { ThrowIfDisposed(); if (rgpPrograms.Length != 1) { throw new ArgumentException("Zero or more than one programs", "rgpPrograms"); } _program = rgpPrograms[0] as RDebugPortSupplier.DebugProgram; if (_program == null) { throw new ArgumentException("rgpPrograms[0] must be an " + nameof(RDebugPortSupplier.DebugProgram), "rgpPrograms"); } Marshal.ThrowExceptionForHR(_program.GetProgramId(out _programId)); _events = pCallback; DebugSession = TaskExtensions.RunSynchronouslyOnUIThread(ct => _program.Session.TraceExecutionAsync(ct)); MainThread = new AD7Thread(this); IsConnected = true; // Enable breakpoint instrumentation. TaskExtensions.RunSynchronouslyOnUIThread(ct => DebugSession.EnableBreakpointsAsync(true, ct)); // Send notification after acquiring the session - we need it in case there were any breakpoints pending before // the attach, in which case we'll immediately get breakpoint creation requests as soon as we send these, and // we will need the session to process them. AD7EngineCreateEvent.Send(this); AD7ProgramCreateEvent.Send(this); Send(new AD7LoadCompleteEvent(), AD7LoadCompleteEvent.IID); // Register event handlers after notifying VS that debug engine has loaded. This order is important because // we may get a Browse event immediately, and we want to raise a breakpoint notification in response to that // to pause the debugger - but it will be ignored unless the engine has reported its creation. // Also, AfterRequest must be registered before Browse, so that we never get in a situation where we get // Browse but not AfterRequest that follows it because of a race between raising and registration. DebugSession.Session.AfterRequest += RSession_AfterRequest; DebugSession.Session.Disconnected += RSession_Disconnected; // If we're already at the Browse prompt, registering the handler will result in its immediate invocation. // We want to handle that fully before we process any following AfterRequest event to avoid concurrency issues // where we pause and never resume, so hold the lock while adding the handler. lock (_browseLock) { DebugSession.Browse += Session_Browse; } return(VSConstants.S_OK); }
int IDebugProgram2.Step(IDebugThread2 pThread, enum_STEPKIND sk, enum_STEPUNIT Step) { ThrowIfDisposed(); Task <bool> step; switch (sk) { case enum_STEPKIND.STEP_OVER: step = DebugSession.StepOverAsync(); break; case enum_STEPKIND.STEP_INTO: step = DebugSession.StepIntoAsync(); break; case enum_STEPKIND.STEP_OUT: goto default; // step = DebugSession.StepOutAsync(); // break; default: return(VSConstants.E_NOTIMPL); } step.ContinueWith(t => { // If step was interrupted midway (e.g. by a breakpoint), we have already reported breakpoint // hit event, and so we must not report step complete. Note that interrupting is not the same // as canceling, and if step was canceled, we must report step completion. bool completed = true; try { completed = t.GetAwaiter().GetResult(); } catch (OperationCanceledException) { } catch (MessageTransportException) { } if (completed) { Send(new AD7SteppingCompleteEvent(), AD7SteppingCompleteEvent.IID); } }); return(VSConstants.S_OK); }
int IDebugProgram2.Detach() { ThrowIfDisposed(); try { // Queue disabling breakpoint instrumentation, but do not wait for it to complete - // if there's currently some code running, this may take a while, so just detach and // let breakpoints be taken care of later. DebugSession.EnableBreakpointsAsync(false) .SilenceException <MessageTransportException>() .DoNotWait(); } finally { // Detach should never fail, even if something above didn't work. DestroyProgram(); } return(VSConstants.S_OK); }
internal DebugBreakpoint(DebugSession session, DebugBreakpointLocation location) { Session = session; Location = location; }
int IDebugProgram3.ExecuteOnThread(IDebugThread2 pThread) { ThrowIfDisposed(); DebugSession.CancelStep(); return(Continue(pThread)); }